diff --git a/.forgejo/workflows/deps.yml b/.forgejo/workflows/deps.yml deleted file mode 100644 index b29723c0..00000000 --- a/.forgejo/workflows/deps.yml +++ /dev/null @@ -1,77 +0,0 @@ -# SPDX-License-Identifier: LGPL-3.0-or-later -# See Notices.txt for copyright information -on: - workflow_call: - outputs: - cache-primary-key: - value: ${{ jobs.deps.outputs.cache-primary-key }} - -jobs: - deps: - runs-on: debian-12 - outputs: - cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} - steps: - - uses: https://git.libre-chip.org/mirrors/checkout@v3 - with: - fetch-depth: 0 - - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 - id: restore-deps - with: - path: deps - key: ${{ github.repository }}-deps-${{ runner.os }}-${{ hashFiles('.forgejo/workflows/deps.yml') }} - lookup-only: true - - name: Install Apt packages - if: steps.restore-deps.outputs.cache-hit != 'true' - run: | - apt-get update -qq - apt-get install -qq \ - bison \ - build-essential \ - ccache \ - clang \ - cvc5 \ - flex \ - gawk \ - g++ \ - git \ - libboost-filesystem-dev \ - libboost-python-dev \ - libboost-system-dev \ - libffi-dev \ - libreadline-dev \ - lld \ - pkg-config \ - python3 \ - python3-click \ - tcl-dev \ - zlib1g-dev - - name: Install Firtool - if: steps.restore-deps.outputs.cache-hit != 'true' - run: | - mkdir -p deps - wget -O deps/firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz - sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 deps/firrtl.tar.gz' - tar -C deps -xvaf deps/firrtl.tar.gz - rm -rf deps/firtool - mv deps/firtool-1.86.0 deps/firtool - - name: Get SymbiYosys - if: steps.restore-deps.outputs.cache-hit != 'true' - run: | - 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://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://git.libre-chip.org/mirrors/yosys deps/yosys - make -C deps/yosys -j"$(nproc)" - - uses: https://git.libre-chip.org/mirrors/cache/save@v3 - if: steps.restore-deps.outputs.cache-hit != 'true' - with: - path: deps - key: ${{ steps.restore-deps.outputs.cache-primary-key }} diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 294ccaa5..001168f4 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -3,57 +3,16 @@ on: [push, pull_request] jobs: - deps: - runs-on: debian-12 - uses: ./.forgejo/workflows/deps.yml test: runs-on: debian-12 - needs: deps + container: + image: git.libre-chip.org/libre-chip/fayalite-deps:latest steps: - - uses: https://git.libre-chip.org/mirrors/checkout@v3 + - uses: actions/checkout@v3 with: fetch-depth: 0 - run: | scripts/check-copyright.sh - - run: | - apt-get update -qq - apt-get install -qq \ - bison \ - build-essential \ - ccache \ - clang \ - cvc5 \ - flex \ - gawk \ - git \ - libboost-filesystem-dev \ - libboost-python-dev \ - libboost-system-dev \ - libffi-dev \ - libreadline-dev \ - lld \ - pkg-config \ - python3 \ - python3-click \ - tcl-dev \ - z3 \ - zlib1g-dev - - run: | - 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://git.libre-chip.org/mirrors/cache/restore@v3 - with: - path: deps - key: ${{ needs.deps.outputs.cache-primary-key }} - fail-on-cache-miss: true - - run: | - make -C deps/z3/build install - make -C deps/sby install - make -C deps/yosys install - export PATH="$(realpath deps/firtool/bin):$PATH" - echo "$PATH" >> "$GITHUB_PATH" - uses: https://git.libre-chip.org/mirrors/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/master' }} @@ -62,3 +21,5 @@ jobs: - 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 + - run: cargo run --example blinky yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/blinky-out + - run: cargo run --example tx_only_uart yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/tx_only_uart-out diff --git a/Cargo.lock b/Cargo.lock index e0c32e94..be5f3bcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "allocator-api2" @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -81,6 +81,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "basic-toml" version = "0.1.8" @@ -149,9 +155,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.9" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -159,9 +165,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstream", "anstyle", @@ -170,10 +176,19 @@ dependencies = [ ] [[package]] -name = "clap_derive" -version = "4.5.8" +name = "clap_complete" +version = "4.5.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "75bf0b32ad2e152de789bb635ea4d3078f6b838ad7974143e99b99f45a04af4a" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_derive" +version = "4.5.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -183,9 +198,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" @@ -291,9 +306,11 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" name = "fayalite" version = "0.3.0" dependencies = [ + "base64", "bitvec", "blake3", "clap", + "clap_complete", "ctor", "eyre", "fayalite-proc-macros", @@ -302,7 +319,7 @@ dependencies = [ "jobslot", "num-bigint", "num-traits", - "os_pipe", + "ordered-float", "petgraph", "serde", "serde_json", @@ -377,12 +394,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", + "r-efi", "wasi", ] @@ -449,23 +467,23 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobslot" -version = "0.2.19" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d" +checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c" dependencies = [ "cfg-if", "derive_destructure2", "getrandom", "libc", "scopeguard", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "libc" -version = "0.2.153" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "linux-raw-sys" @@ -508,13 +526,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "os_pipe" -version = "1.2.1" +name = "ordered-float" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" dependencies = [ - "libc", - "windows-sys 0.59.0", + "num-traits", + "rand", + "serde", ] [[package]] @@ -557,12 +576,37 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", + "serde", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "serde", +] + [[package]] name = "rustix" version = "0.38.31" @@ -748,9 +792,21 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.14.7+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] [[package]] name = "which" @@ -795,6 +851,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" version = "0.52.0" @@ -806,11 +868,11 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-targets", + "windows-link", ] [[package]] @@ -883,6 +945,12 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 5a792c6f..2380ea7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,17 +18,19 @@ fayalite-proc-macros = { version = "=0.3.0", path = "crates/fayalite-proc-macros fayalite-proc-macros-impl = { version = "=0.3.0", path = "crates/fayalite-proc-macros-impl" } fayalite-visit-gen = { version = "=0.3.0", path = "crates/fayalite-visit-gen" } base16ct = "0.2.0" +base64 = "0.22.1" bitvec = { version = "1.0.1", features = ["serde"] } blake3 = { version = "1.5.4", features = ["serde"] } clap = { version = "4.5.9", features = ["derive", "env", "string"] } +clap_complete = "4.5.58" ctor = "0.2.8" eyre = "0.6.12" hashbrown = "0.15.2" indexmap = { version = "2.5.0", features = ["serde"] } -jobslot = "0.2.19" +jobslot = "0.2.23" num-bigint = "0.4.6" num-traits = "0.2.16" -os_pipe = "1.2.1" +ordered-float = { version = "5.1.0", features = ["serde"] } petgraph = "0.8.1" prettyplease = "0.2.20" proc-macro2 = "1.0.83" diff --git a/README.md b/README.md index 0b91833e..18cd78c3 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,73 @@ Fayalite is a library for designing digital hardware -- a hardware description l [FIRRTL]: https://github.com/chipsalliance/firrtl-spec +# Building the [Blinky example] for the Arty A7 100T on Linux + +[Blinky example]: crates/fayalite/examples/blinky.rs + +This uses the container image containing all the external programs and files that Fayalite needs to build for FPGAs, the sources for the container image are in + +Steps: + +Install podman (or docker). + +Run: +```bash +podman run --rm --security-opt label=disable --volume="$(pwd):$(pwd)" -w="$(pwd)" -it git.libre-chip.org/libre-chip/fayalite-deps:latest cargo run --example blinky yosys-nextpnr-xray --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db --platform arty-a7-100t -o target/blinky-out +``` + +To actually program the FPGA, you'll need to install [openFPGALoader] on your host OS: + +[openFPGALoader]: https://github.com/trabucayre/openFPGALoader + +On Debian 12: +```bash +sudo apt update && sudo apt install openfpgaloader +``` + +Then program the FPGA: +```bash +sudo openFPGALoader --board arty_a7_100t target/blinky-out/blinky.bit +``` + +This will program the FPGA but leave the Flash chip unmodified, so the FPGA will revert when the board is power-cycled. + +To program the Flash also, so it stays programmed when power-cycling the board: + +```bash +sudo openFPGALoader --board arty_a7_100t -f target/blinky-out/blinky.bit +``` + +# Building the [Transmit-only UART example] for the Arty A7 100T on Linux + +[Transmit-only UART example]: crates/fayalite/examples/tx_only_uart.rs + +Follow the steps above of building the Blinky example, but replace `blinky` with `tx_only_uart`. + +View the output using [tio](https://github.com/tio/tio) which you can install in Debian using `apt`. + +Find the correct USB device: +```bash +sudo tio --list +``` + +You want the device with a name like (note the `if01`, `if00` is presumably the JTAG port): +`/dev/serial/by-id/usb-Digilent_Digilent_USB_Device_210319B4A51E-if01-port0` + +Connect to the serial port: +```bash +sudo tio -b115200 /dev/serial/by-id/put-your-device-id-here +``` + +You'll see (repeating endlessly): +```text +Hello World from Fayalite!!! +Hello World from Fayalite!!! +Hello World from Fayalite!!! +``` + +Press Ctrl+T then `q` to exit tio. + # Funding ## NLnet Grants diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 538c2da2..97fa3ffa 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -87,7 +87,11 @@ impl ParsedBundle { no_static: _, no_runtime_generics: _, cmp_eq: _, + ref get, } = options.body; + if let Some((get, ..)) = get { + errors.error(get, "#[hdl(get(...))] is not allowed on structs"); + } let mut fields = match fields { syn::Fields::Named(fields) => fields, syn::Fields::Unnamed(fields) => { @@ -220,7 +224,7 @@ impl Builder { .args .push_value(match get_field_state(field_index) { BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=> - ::fayalite::bundle::Unfilled<#ty> + () }, BuilderFieldState::Generic => { let type_var = type_var_for_field_name(ident); @@ -345,7 +349,6 @@ impl ToTokens for Builder { } })); quote_spanned! {self.ident.span()=> - #[automatically_derived] #[allow(non_camel_case_types, non_snake_case, dead_code)] impl #impl_generics #unfilled_ty #where_clause @@ -380,7 +383,7 @@ impl ToTokens for Builder { fn default() -> Self { #ident { #phantom_field_name: ::fayalite::__std::marker::PhantomData, - #(#field_idents: ::fayalite::__std::default::Default::default(),)* + #(#field_idents: (),)* } } } @@ -392,16 +395,30 @@ impl ToTokens for Builder { let type_generics = self.generics.split_for_impl().1; quote_spanned! {self.ident.span()=> #[automatically_derived] - #[allow(non_camel_case_types, dead_code)] - impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty + #[allow(non_camel_case_types, dead_code, private_interfaces)] + impl #filled_impl_generics ::fayalite::expr::ValueType for #filled_ty #filled_where_clause { type Type = #target #type_generics; + type ValueCategory = ::fayalite::expr::value_category::ValueCategoryExpr; + + fn ty(&self) -> ::Type { + #target { + #(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)* + } + } + } + + #[automatically_derived] + #[allow(non_camel_case_types, dead_code, private_interfaces)] + impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty + #filled_where_clause + { fn to_expr( &self, - ) -> ::fayalite::expr::Expr<::Type> { + ) -> ::fayalite::expr::Expr<::Type> { let __ty = #target { - #(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)* + #(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)* }; let __field_values = [ #(::fayalite::expr::Expr::canonical(self.#field_idents),)* @@ -446,6 +463,7 @@ impl ToTokens for ParsedBundle { no_static, no_runtime_generics, cmp_eq, + get: _, } = &options.body; let target = get_target(target, ident); let mut item_attrs = attrs.clone(); @@ -495,7 +513,6 @@ impl ToTokens for ParsedBundle { }; builder.to_tokens(tokens); let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); - let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled); let mut mask_type_fields = FieldsNamed::from(fields.clone()); for Field { ty, .. } in &mut mask_type_fields.named { *ty = parse_quote_spanned! {span=> @@ -513,8 +530,6 @@ impl ToTokens for ParsedBundle { mask_type_builder.to_tokens(tokens); let unfilled_mask_type_builder_ty = mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); - let filled_mask_type_builder_ty = - mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled); ItemStruct { attrs: vec![ common_derives(span), @@ -694,10 +709,10 @@ impl ToTokens for ParsedBundle { v.field(&value.#ident); } })); - let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let value_type_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), + #ident: ::fayalite::expr::ValueType::ty(&self.#ident), } })); let fields_len = fields.named().into_iter().len(); @@ -781,7 +796,6 @@ impl ToTokens for ParsedBundle { #where_clause { type Builder = #unfilled_mask_type_builder_ty; - type FilledBuilder = #filled_mask_type_builder_ty; fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) } @@ -806,28 +820,39 @@ impl ToTokens for ParsedBundle { } } #[automatically_derived] - impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics + impl #impl_generics ::fayalite::expr::ValueType for #mask_type_sim_value_ident #type_generics #where_clause { type Type = #mask_type_ident #type_generics; + type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue; + fn ty(&self) -> ::Type { + #mask_type_ident { + #(#value_type_fields)* + } + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics + #where_clause + { fn to_sim_value( &self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { let ty = #mask_type_ident { - #(#to_sim_value_fields)* + #(#value_type_fields)* }; ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) } fn into_sim_value( self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { let ty = #mask_type_ident { - #(#to_sim_value_fields)* + #(#value_type_fields)* }; ::fayalite::sim::value::SimValue::from_value(ty, self) } @@ -931,7 +956,6 @@ impl ToTokens for ParsedBundle { #where_clause { type Builder = #unfilled_builder_ty; - type FilledBuilder = #filled_builder_ty; fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) } @@ -956,28 +980,39 @@ impl ToTokens for ParsedBundle { } } #[automatically_derived] - impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics + impl #impl_generics ::fayalite::expr::ValueType for #sim_value_ident #type_generics #where_clause { type Type = #target #type_generics; + type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue; + fn ty(&self) -> ::Type { + #target { + #(#value_type_fields)* + } + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics + #where_clause + { fn to_sim_value( &self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { let ty = #target { - #(#to_sim_value_fields)* + #(#value_type_fields)* }; ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) } fn into_sim_value( self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { let ty = #target { - #(#to_sim_value_fields)* + #(#value_type_fields)* }; ::fayalite::sim::value::SimValue::from_value(ty, self) } @@ -1003,91 +1038,172 @@ impl ToTokens for ParsedBundle { } .to_tokens(tokens); if let Some((cmp_eq,)) = cmp_eq { - let mut expr_where_clause = + let mut cmp_eq_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![]; + let mut fields_value_eq = vec![]; + let mut fields_value_ne = vec![]; + let mut fields_expr_eq = vec![]; + let mut fields_expr_ne = vec![]; + let mut fields_valueless_eq = vec![]; + let mut fields_valueless_ne = vec![]; for field in fields.named() { let field_ident = field.ident(); let field_ty = field.ty(); - expr_where_clause + cmp_eq_where_clause .predicates .push(parse_quote_spanned! {cmp_eq.span=> - #field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty> + #field_ty: ::fayalite::expr::HdlPartialEqImpl<#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_value_eq.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_value_eq( + __lhs.#field_ident, + ::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident), + __rhs.#field_ident, + ::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident), + ) }); - fields_cmp_eq.push(quote_spanned! {span=> - ::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident) + fields_value_ne.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_value_ne( + __lhs.#field_ident, + ::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident), + __rhs.#field_ident, + ::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident), + ) }); - fields_cmp_ne.push(quote_spanned! {span=> - ::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident) + fields_expr_eq.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq( + __lhs.#field_ident, + __rhs.#field_ident, + ) + }); + fields_expr_ne.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_expr_ne( + __lhs.#field_ident, + __rhs.#field_ident, + ) + }); + fields_valueless_eq.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq( + ::fayalite::expr::Valueless::new(__lhs.#field_ident), + ::fayalite::expr::Valueless::new(__rhs.#field_ident), + ) + }); + fields_valueless_ne.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_valueless_ne( + ::fayalite::expr::Valueless::new(__lhs.#field_ident), + ::fayalite::expr::Valueless::new(__rhs.#field_ident), + ) }); } - let sim_value_eq_body; - let cmp_eq_body; - let cmp_ne_body; + let value_eq_body; + let value_ne_body; + let expr_eq_body; + let expr_ne_body; + let valueless_eq_body; + let valueless_ne_body; if fields_len == 0 { - sim_value_eq_body = quote_spanned! {span=> + value_eq_body = quote_spanned! {span=> true }; - cmp_eq_body = quote_spanned! {span=> + value_ne_body = quote_spanned! {span=> + false + }; + expr_eq_body = quote_spanned! {span=> ::fayalite::expr::ToExpr::to_expr(&true) }; - cmp_ne_body = quote_spanned! {span=> + expr_ne_body = quote_spanned! {span=> ::fayalite::expr::ToExpr::to_expr(&false) }; + valueless_eq_body = quote_spanned! {span=> + ::fayalite::expr::Valueless::new(::fayalite::int::Bool) + }; + valueless_ne_body = quote_spanned! {span=> + ::fayalite::expr::Valueless::new(::fayalite::int::Bool) + }; } else { - sim_value_eq_body = quote_spanned! {span=> - #(#fields_sim_value_eq)&&* + value_eq_body = quote_spanned! {span=> + #(#fields_value_eq)&* }; - cmp_eq_body = quote_spanned! {span=> - #(#fields_cmp_eq)&* + value_ne_body = quote_spanned! {span=> + #(#fields_value_ne)|* }; - cmp_ne_body = quote_spanned! {span=> - #(#fields_cmp_ne)|* + expr_eq_body = quote_spanned! {span=> + #(#fields_expr_eq)&* + }; + expr_ne_body = quote_spanned! {span=> + #(#fields_expr_ne)|* + }; + valueless_eq_body = quote_spanned! {span=> + let __lhs = ::fayalite::expr::ValueType::ty(&__lhs); + let __rhs = ::fayalite::expr::ValueType::ty(&__rhs); + #(#fields_valueless_eq)|* + }; + valueless_ne_body = quote_spanned! {span=> + let __lhs = ::fayalite::expr::ValueType::ty(&__lhs); + let __rhs = ::fayalite::expr::ValueType::ty(&__rhs); + #(#fields_valueless_ne)|* }; }; quote_spanned! {span=> #[automatically_derived] - impl #impl_generics ::fayalite::expr::ops::ExprPartialEq for #target #type_generics - #expr_where_clause + impl #impl_generics ::fayalite::expr::HdlPartialEqImpl for #target #type_generics + #cmp_eq_where_clause { - fn cmp_eq( + #[track_caller] + fn cmp_value_eq( + __lhs: Self, + __lhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + __rhs: Self, + __rhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + ) -> ::fayalite::__std::primitive::bool { + #value_eq_body + } + + #[track_caller] + fn cmp_value_ne( + __lhs: Self, + __lhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + __rhs: Self, + __rhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + ) -> ::fayalite::__std::primitive::bool { + #value_ne_body + } + + #[track_caller] + fn cmp_expr_eq( __lhs: ::fayalite::expr::Expr, __rhs: ::fayalite::expr::Expr, ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { - #cmp_eq_body + #expr_eq_body } - fn cmp_ne( + + #[track_caller] + fn cmp_expr_ne( __lhs: ::fayalite::expr::Expr, __rhs: ::fayalite::expr::Expr, ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { - #cmp_ne_body + #expr_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 + + #[track_caller] + fn cmp_valueless_eq( + __lhs: ::fayalite::expr::Valueless, + __rhs: ::fayalite::expr::Valueless, + ) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> { + #valueless_eq_body + } + + #[track_caller] + fn cmp_valueless_ne( + __lhs: ::fayalite::expr::Valueless, + __rhs: ::fayalite::expr::Valueless, + ) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> { + #valueless_ne_body } } } diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index e5cbe277..90838f0b 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -159,10 +159,14 @@ impl ParsedEnum { no_static: _, no_runtime_generics: _, cmp_eq, + ref get, } = options.body; if let Some((cmp_eq,)) = cmp_eq { errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums"); } + if let Some((get, ..)) = get { + errors.error(get, "#[hdl(get(...))] is not allowed on enums"); + } attrs.retain(|attr| { if attr.path().is_ident("repr") { errors.error(attr, "#[repr] is not supported on #[hdl] enums"); @@ -225,6 +229,7 @@ impl ToTokens for ParsedEnum { no_static, no_runtime_generics, cmp_eq: _, // TODO: implement cmp_eq for enums + get: _, } = &options.body; let target = get_target(target, ident); let mut struct_attrs = attrs.clone(); @@ -549,7 +554,6 @@ impl ToTokens for ParsedEnum { for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { if let Some(ParsedVariantField { ty, .. }) = field { quote_spanned! {span=> - #[automatically_derived] impl #impl_generics #target #type_generics #where_clause { @@ -571,7 +575,6 @@ impl ToTokens for ParsedEnum { ) } } - #[automatically_derived] impl #impl_generics #sim_builder_ident #type_generics #where_clause { @@ -593,7 +596,6 @@ impl ToTokens for ParsedEnum { } } else { quote_spanned! {span=> - #[automatically_derived] impl #impl_generics #target #type_generics #where_clause { @@ -608,7 +610,6 @@ impl ToTokens for ParsedEnum { ) } } - #[automatically_derived] impl #impl_generics #sim_builder_ident #type_generics #where_clause { @@ -1023,16 +1024,26 @@ impl ToTokens for ParsedEnum { <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; } #[automatically_derived] - impl #static_impl_generics ::fayalite::sim::value::ToSimValue + impl #static_impl_generics ::fayalite::expr::ValueType for #sim_value_ident #static_type_generics #static_where_clause { type Type = #target #static_type_generics; + type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue; + fn ty(&self) -> ::Type { + ::fayalite::ty::StaticType::TYPE + } + } + #[automatically_derived] + impl #static_impl_generics ::fayalite::sim::value::ToSimValue + for #sim_value_ident #static_type_generics + #static_where_clause + { fn to_sim_value( &self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { ::fayalite::sim::value::SimValue::from_value( ::fayalite::ty::StaticType::TYPE, @@ -1042,7 +1053,7 @@ impl ToTokens for ParsedEnum { fn into_sim_value( self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { ::fayalite::sim::value::SimValue::from_value( ::fayalite::ty::StaticType::TYPE, 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 d4a035bd..0fa2222d 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs @@ -3,29 +3,264 @@ use crate::{ Errors, HdlAttr, hdl_type_common::{ - ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, TypesParser, - get_target, + ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, + PhantomConstGetBound, TypesParser, WrappedInConst, common_derives, get_target, known_items, }, kw, }; use proc_macro2::TokenStream; -use quote::ToTokens; -use syn::{Attribute, Generics, Ident, ItemType, Token, Type, Visibility, parse_quote_spanned}; +use quote::{ToTokens, format_ident, quote_spanned}; +use syn::{ + Attribute, Expr, Fields, GenericParam, Generics, Ident, ItemStruct, ItemType, Token, Type, + TypeGroup, TypeParam, TypeParen, Visibility, parse_quote_spanned, punctuated::Pair, + token::Paren, +}; #[derive(Clone, Debug)] -pub(crate) struct ParsedTypeAlias { - pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, - pub(crate) vis: Visibility, - pub(crate) type_token: Token![type], - pub(crate) ident: Ident, - pub(crate) generics: MaybeParsed, - pub(crate) eq_token: Token![=], - pub(crate) ty: MaybeParsed, - pub(crate) semi_token: Token![;], +pub(crate) struct PhantomConstAccessorTypeParam { + attrs: Vec, + ident: Ident, + colon_token: Token![:], + phantom_const_get_bound: PhantomConstGetBound, + plus_token: Option, +} + +impl From for TypeParam { + fn from(value: PhantomConstAccessorTypeParam) -> Self { + let PhantomConstAccessorTypeParam { + attrs, + ident, + colon_token, + phantom_const_get_bound, + plus_token, + } = value; + TypeParam { + attrs, + ident, + colon_token: Some(colon_token), + bounds: FromIterator::from_iter([Pair::new( + phantom_const_get_bound.into(), + plus_token, + )]), + eq_token: None, + default: None, + } + } +} + +impl From for GenericParam { + fn from(value: PhantomConstAccessorTypeParam) -> Self { + TypeParam::from(value).into() + } +} + +impl PhantomConstAccessorTypeParam { + fn parse_opt(generic_param: GenericParam) -> Option { + let GenericParam::Type(TypeParam { + attrs, + ident, + colon_token, + bounds, + eq_token: None, + default: None, + }) = generic_param + else { + return None; + }; + let colon_token = colon_token.unwrap_or(Token![:](ident.span())); + let mut bounds = bounds.into_pairs(); + let (bound, plus_token) = bounds.next()?.into_tuple(); + let phantom_const_get_bound = PhantomConstGetBound::parse_type_param_bound(bound) + .ok()? + .ok()?; + let None = bounds.next() else { + return None; + }; + Some(Self { + attrs, + ident, + colon_token, + phantom_const_get_bound, + plus_token, + }) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct PhantomConstAccessorGenerics { + lt_token: Token![<], + type_param: PhantomConstAccessorTypeParam, + comma_token: Option, + gt_token: Token![>], +} + +impl From for Generics { + fn from(value: PhantomConstAccessorGenerics) -> Self { + let PhantomConstAccessorGenerics { + lt_token, + type_param, + comma_token, + gt_token, + } = value; + Generics { + lt_token: Some(lt_token), + params: FromIterator::from_iter([Pair::new(type_param.into(), comma_token)]), + gt_token: Some(gt_token), + where_clause: None, + } + } +} + +impl<'a> From<&'a PhantomConstAccessorGenerics> for Generics { + fn from(value: &'a PhantomConstAccessorGenerics) -> Self { + value.clone().into() + } +} + +impl PhantomConstAccessorGenerics { + fn parse_opt(generics: Generics) -> Option { + let Generics { + lt_token, + params, + gt_token, + where_clause: None, + } = generics + else { + return None; + }; + let mut params = params.into_pairs(); + let (generic_param, comma_token) = params.next()?.into_tuple(); + let type_param = PhantomConstAccessorTypeParam::parse_opt(generic_param)?; + let span = type_param.ident.span(); + let lt_token = lt_token.unwrap_or(Token![<](span)); + let gt_token = gt_token.unwrap_or(Token![>](span)); + let None = params.next() else { + return None; + }; + Some(Self { + lt_token, + type_param, + comma_token, + gt_token, + }) + } +} + +#[derive(Clone, Debug)] +pub(crate) enum ParsedTypeAlias { + TypeAlias { + attrs: Vec, + options: HdlAttr, + vis: Visibility, + type_token: Token![type], + ident: Ident, + generics: MaybeParsed, + eq_token: Token![=], + ty: MaybeParsed, + semi_token: Token![;], + }, + PhantomConstAccessor { + attrs: Vec, + options: HdlAttr, + get: (kw::get, Paren, Expr), + vis: Visibility, + type_token: Token![type], + ident: Ident, + generics: PhantomConstAccessorGenerics, + eq_token: Token![=], + ty: Type, + ty_is_dyn_size: Option, + semi_token: Token![;], + }, } impl ParsedTypeAlias { + fn ty_is_dyn_size(ty: &Type) -> Option { + match ty { + Type::Group(TypeGroup { + group_token: _, + elem, + }) => Self::ty_is_dyn_size(elem), + Type::Paren(TypeParen { + paren_token: _, + elem, + }) => Self::ty_is_dyn_size(elem), + Type::Path(syn::TypePath { qself: None, path }) => { + known_items::DynSize::parse_path(path.clone()).ok() + } + _ => None, + } + } + fn parse_phantom_const_accessor( + item: ItemType, + mut errors: Errors, + options: HdlAttr, + get: (kw::get, Paren, Expr), + ) -> syn::Result { + let ItemType { + attrs, + vis, + type_token, + ident, + generics, + eq_token, + ty, + semi_token, + } = item; + let ItemOptions { + outline_generated: _, + ref target, + custom_bounds, + no_static, + no_runtime_generics, + cmp_eq, + get: _, + } = options.body; + if let Some((no_static,)) = no_static { + errors.error(no_static, "no_static is not valid on type aliases"); + } + if let Some((target, ..)) = target { + errors.error( + target, + "target is not implemented on PhantomConstGet type aliases", + ); + } + if let Some((no_runtime_generics,)) = no_runtime_generics { + errors.error( + no_runtime_generics, + "no_runtime_generics is not implemented on PhantomConstGet type aliases", + ); + } + if let Some((cmp_eq,)) = cmp_eq { + errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); + } + if let Some((custom_bounds,)) = custom_bounds { + errors.error( + custom_bounds, + "custom_bounds is not implemented on PhantomConstGet type aliases", + ); + } + let Some(generics) = PhantomConstAccessorGenerics::parse_opt(generics) else { + errors.error(ident, "#[hdl(get(...))] type alias must be of the form:\ntype MyTypeGetter> = RetType;"); + errors.finish()?; + unreachable!(); + }; + errors.finish()?; + let ty_is_dyn_size = Self::ty_is_dyn_size(&ty); + Ok(Self::PhantomConstAccessor { + attrs, + options, + get, + vis, + type_token, + ident, + generics, + eq_token, + ty: *ty, + ty_is_dyn_size, + semi_token, + }) + } fn parse(item: ItemType) -> syn::Result { let ItemType { mut attrs, @@ -51,7 +286,25 @@ impl ParsedTypeAlias { no_static, no_runtime_generics: _, cmp_eq, + ref mut get, } = options.body; + if let Some(get) = get.take() { + return Self::parse_phantom_const_accessor( + ItemType { + attrs, + vis, + type_token, + ident, + generics, + eq_token, + ty, + semi_token, + }, + errors, + options, + get, + ); + } if let Some((no_static,)) = no_static { errors.error(no_static, "no_static is not valid on type aliases"); } @@ -67,7 +320,7 @@ impl ParsedTypeAlias { }; let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors); errors.finish()?; - Ok(Self { + Ok(Self::TypeAlias { attrs, options, vis, @@ -83,54 +336,155 @@ impl ParsedTypeAlias { impl ToTokens for ParsedTypeAlias { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - attrs, - options, - vis, - type_token, - ident, - generics, - eq_token, - ty, - semi_token, - } = self; - let ItemOptions { - outline_generated: _, - target, - custom_bounds: _, - no_static: _, - no_runtime_generics, - cmp_eq: _, - } = &options.body; - let target = get_target(target, ident); - let mut type_attrs = attrs.clone(); - type_attrs.push(parse_quote_spanned! {ident.span()=> - #[allow(type_alias_bounds)] - }); - ItemType { - attrs: type_attrs, - vis: vis.clone(), - type_token: *type_token, - ident: ident.clone(), - generics: generics.into(), - eq_token: *eq_token, - ty: Box::new(ty.clone().into()), - semi_token: *semi_token, - } - .to_tokens(tokens); - if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(ty), None) = - (generics, ty, no_runtime_generics) - { - generics.make_runtime_generics(tokens, vis, ident, &target, |context| { - ty.make_hdl_type_expr(context) - }) + match self { + Self::TypeAlias { + attrs, + options, + vis, + type_token, + ident, + generics, + eq_token, + ty, + semi_token, + } => { + let ItemOptions { + outline_generated: _, + target, + custom_bounds: _, + no_static: _, + no_runtime_generics, + cmp_eq: _, + get: _, + } = &options.body; + let target = get_target(target, ident); + let mut type_attrs = attrs.clone(); + type_attrs.push(parse_quote_spanned! {ident.span()=> + #[allow(type_alias_bounds)] + }); + ItemType { + attrs: type_attrs, + vis: vis.clone(), + type_token: *type_token, + ident: ident.clone(), + generics: generics.into(), + eq_token: *eq_token, + ty: Box::new(ty.clone().into()), + semi_token: *semi_token, + } + .to_tokens(tokens); + if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(ty), None) = + (generics, ty, no_runtime_generics) + { + generics.make_runtime_generics(tokens, vis, ident, &target, |context| { + ty.make_hdl_type_expr(context) + }) + } + } + Self::PhantomConstAccessor { + attrs, + options, + get: (_get_kw, _get_paren, get_expr), + vis, + type_token, + ident, + generics, + eq_token, + ty, + ty_is_dyn_size, + semi_token, + } => { + let ItemOptions { + outline_generated: _, + target: _, + custom_bounds: _, + no_static: _, + no_runtime_generics: _, + cmp_eq: _, + get: _, + } = &options.body; + let span = ident.span(); + let mut type_attrs = attrs.clone(); + type_attrs.push(parse_quote_spanned! {span=> + #[allow(type_alias_bounds)] + }); + let type_param_ident = &generics.type_param.ident; + let syn_generics = Generics::from(generics); + ItemType { + attrs: type_attrs, + vis: vis.clone(), + type_token: *type_token, + ident: ident.clone(), + generics: syn_generics.clone(), + eq_token: *eq_token, + ty: parse_quote_spanned! {span=> + <#ty as ::fayalite::phantom_const::ReturnSelfUnchanged<#type_param_ident>>::Type + }, + semi_token: *semi_token, + } + .to_tokens(tokens); + let generics_accumulation_ident = + format_ident!("__{}__GenericsAccumulation", ident); + ItemStruct { + attrs: vec![ + common_derives(span), + parse_quote_spanned! {span=> + #[allow(non_camel_case_types)] + }, + ], + vis: vis.clone(), + struct_token: Token![struct](span), + ident: generics_accumulation_ident.clone(), + generics: Generics::default(), + fields: Fields::Unnamed(parse_quote_spanned! {span=> + (()) + }), + semi_token: Some(Token![;](span)), + } + .to_tokens(tokens); + quote_spanned! {span=> + #[allow(non_upper_case_globals, dead_code)] + #vis const #ident: #generics_accumulation_ident = #generics_accumulation_ident(()); + } + .to_tokens(tokens); + let mut wrapped_in_const = WrappedInConst::new(tokens, span); + let tokens = wrapped_in_const.inner(); + let (impl_generics, _type_generics, where_clause) = syn_generics.split_for_impl(); + let phantom_const_get_ty = &generics.type_param.phantom_const_get_bound.ty; + let index_output = if let Some(ty_is_dyn_size) = ty_is_dyn_size { + known_items::usize(ty_is_dyn_size.span).to_token_stream() + } else { + ty.to_token_stream() + }; + quote_spanned! {span=> + #[allow(non_upper_case_globals)] + #[automatically_derived] + impl #impl_generics ::fayalite::__std::ops::Index<#type_param_ident> + for #generics_accumulation_ident + #where_clause + { + type Output = #index_output; + + fn index(&self, __param: #type_param_ident) -> &Self::Output { + ::fayalite::phantom_const::type_alias_phantom_const_get_helper::<#phantom_const_get_ty, #index_output>( + __param, + #get_expr, + ) + } + } + } + .to_tokens(tokens); + } } } } pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result { let item = ParsedTypeAlias::parse(item)?; - let outline_generated = item.options.body.outline_generated; + let outline_generated = match &item { + ParsedTypeAlias::TypeAlias { options, .. } + | ParsedTypeAlias::PhantomConstAccessor { options, .. } => options.body.outline_generated, + }; let mut contents = item.to_token_stream(); if outline_generated.is_some() { contents = crate::outline_generated(contents, "hdl-type-alias-"); 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 1206f11c..3a0e5e9e 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -8,9 +8,9 @@ use syn::{ 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, + Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, TraitBound, Turbofish, + Type, TypeGenerics, TypeGroup, TypeParam, TypeParamBound, TypeParen, TypePath, TypeTuple, + Visibility, WhereClause, WherePredicate, parse::{Parse, ParseStream}, parse_quote, parse_quote_spanned, punctuated::{Pair, Punctuated}, @@ -27,6 +27,7 @@ crate::options! { NoStatic(no_static), NoRuntimeGenerics(no_runtime_generics), CmpEq(cmp_eq), + Get(get, Expr), } } @@ -2045,6 +2046,7 @@ pub(crate) mod known_items { impl_known_item!(::fayalite::int::Size); impl_known_item!(::fayalite::int::UInt); impl_known_item!(::fayalite::int::UIntType); + impl_known_item!(::fayalite::phantom_const::PhantomConstGet); impl_known_item!(::fayalite::reset::ResetType); impl_known_item!(::fayalite::ty::CanonicalType); impl_known_item!(::fayalite::ty::StaticType); @@ -2063,6 +2065,174 @@ pub(crate) mod known_items { ); } +#[derive(Clone, Debug)] +pub(crate) struct PhantomConstGetBound { + pub(crate) phantom_const_get: known_items::PhantomConstGet, + pub(crate) colon2_token: Option, + pub(crate) lt_token: Token![<], + pub(crate) ty: Type, + pub(crate) comma_token: Option, + pub(crate) gt_token: Token![>], +} + +impl PhantomConstGetBound { + pub(crate) fn parse_path_with_arguments(path: Path) -> syn::Result> { + match known_items::PhantomConstGet::parse_path_with_arguments(path) { + Ok((phantom_const_get, arguments)) => { + Self::parse_path_and_arguments(phantom_const_get, arguments).map(Ok) + } + Err(path) => Ok(Err(path)), + } + } + pub(crate) fn parse_path_and_arguments( + phantom_const_get: known_items::PhantomConstGet, + arguments: PathArguments, + ) -> syn::Result { + let error = |arguments: PathArguments, message: &str| { + let mut path = phantom_const_get.path.clone(); + path.segments.last_mut().expect("known to exist").arguments = arguments; + syn::Error::new_spanned(path, message) + }; + match arguments { + PathArguments::None => Err(error(arguments, "missing generics for PhantomConstGet")), + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token, + lt_token, + args, + gt_token, + }) => { + let error = |args: Punctuated, message| { + error( + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token, + lt_token, + args, + gt_token, + }), + message, + ) + }; + let mut args = args.into_pairs().peekable(); + let Some((generic_argument, comma_token)) = args.next().map(Pair::into_tuple) + else { + return Err(error( + Default::default(), + "PhantomConstGet takes a type argument but no generic arguments were supplied", + )); + }; + if args.peek().is_some() { + return Err(error( + [Pair::new(generic_argument, comma_token)] + .into_iter() + .chain(args) + .collect(), + "PhantomConstGet takes a single type argument but too many generic arguments were supplied", + )); + }; + let GenericArgument::Type(ty) = generic_argument else { + return Err(error( + Punctuated::from_iter([Pair::new(generic_argument, comma_token)]), + "PhantomConstGet requires a type argument", + )); + }; + Ok(Self { + phantom_const_get, + colon2_token, + lt_token, + ty, + comma_token, + gt_token, + }) + } + PathArguments::Parenthesized(_) => Err(error( + arguments, + "parenthetical generics are not valid for PhantomConstGet", + )), + } + } + pub(crate) fn parse_type_param_bound( + bound: TypeParamBound, + ) -> syn::Result> { + let TypeParamBound::Trait(TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + }) = bound + else { + return Ok(Err(bound)); + }; + Ok(match Self::parse_path_with_arguments(path)? { + Ok(v) => Ok(v), + Err(path) => Err(TypeParamBound::Trait(TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + })), + }) + } +} + +impl ToTokens for PhantomConstGetBound { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + phantom_const_get, + colon2_token, + lt_token, + ty, + comma_token, + gt_token, + } = self; + phantom_const_get.to_tokens(tokens); + colon2_token.to_tokens(tokens); + lt_token.to_tokens(tokens); + ty.to_tokens(tokens); + comma_token.to_tokens(tokens); + gt_token.to_tokens(tokens); + } +} + +impl From for Path { + fn from(value: PhantomConstGetBound) -> Self { + let PhantomConstGetBound { + phantom_const_get, + colon2_token, + lt_token, + ty, + comma_token, + gt_token, + } = value; + let mut path = phantom_const_get.path; + path.segments.last_mut().expect("known to exist").arguments = + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token, + lt_token, + args: FromIterator::from_iter([Pair::new(GenericArgument::Type(ty), comma_token)]), + gt_token, + }); + path + } +} + +impl From for TraitBound { + fn from(value: PhantomConstGetBound) -> Self { + let path = Path::from(value); + TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + } + } +} + +impl From for TypeParamBound { + fn from(value: PhantomConstGetBound) -> Self { + TraitBound::from(value).into() + } +} + macro_rules! impl_bounds { ( #[struct = $struct_type:ident] @@ -2070,6 +2240,10 @@ macro_rules! impl_bounds { $( $Variant:ident, )* + $( + #[has_body] + $VariantHasBody:ident($variant_has_body_ty:ty), + )* $( #[unknown] $Unknown:ident, @@ -2079,6 +2253,7 @@ macro_rules! impl_bounds { #[derive(Clone, Debug)] $vis enum $enum_type { $($Variant(known_items::$Variant),)* + $($VariantHasBody($variant_has_body_ty),)* $($Unknown(syn::TypeParamBound),)? } @@ -2088,31 +2263,42 @@ macro_rules! impl_bounds { } })* + $(impl From<$variant_has_body_ty> for $enum_type { + fn from(v: $variant_has_body_ty) -> Self { + Self::$VariantHasBody(v) + } + })* + impl ToTokens for $enum_type { fn to_tokens(&self, tokens: &mut TokenStream) { match self { $(Self::$Variant(v) => v.to_tokens(tokens),)* + $(Self::$VariantHasBody(v) => v.to_tokens(tokens),)* $(Self::$Unknown(v) => v.to_tokens(tokens),)? } } } impl $enum_type { - $vis fn parse_path(path: Path) -> Result { + $vis fn parse_path_with_arguments(path: Path) -> syn::Result> { #![allow(unreachable_code)] $(let path = match known_items::$Variant::parse_path(path) { - Ok(v) => return Ok(Self::$Variant(v)), + Ok(v) => return Ok(Ok(Self::$Variant(v))), Err(path) => path, };)* - $(return Ok(Self::$Unknown(syn::TraitBound { + $(let path = match <$variant_has_body_ty>::parse_path_with_arguments(path)? { + Ok(v) => return Ok(Ok(Self::$VariantHasBody(v))), + Err(path) => path, + };)* + $(return Ok(Ok(Self::$Unknown(syn::TraitBound { paren_token: None, modifier: syn::TraitBoundModifier::None, lifetimes: None, path, - }.into()));)? - Err(path) + }.into())));)? + Ok(Err(path)) } - $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result { + $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> syn::Result> { #![allow(unreachable_code)] if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound { if let syn::TraitBound { @@ -2121,24 +2307,24 @@ macro_rules! impl_bounds { lifetimes: None, path: _, } = trait_bound { - match Self::parse_path(trait_bound.path) { - Ok(retval) => return Ok(retval), + match Self::parse_path_with_arguments(trait_bound.path)? { + Ok(retval) => return Ok(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) + $(return Ok(Ok(Self::$Unknown(type_param_bound)));)? + Ok(Err(type_param_bound)) } } impl Parse for $enum_type { fn parse(input: ParseStream) -> syn::Result { - Self::parse_type_param_bound(input.parse()?) + 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(", ")), + format_args!("expected one of: {}", [$(stringify!($Variant),)* $(stringify!($VariantHasBody)),*].join(", ")), )) } } @@ -2147,6 +2333,7 @@ macro_rules! impl_bounds { #[allow(non_snake_case)] $vis struct $struct_type { $($vis $Variant: Option,)* + $($vis $VariantHasBody: Option<$variant_has_body_ty>,)* $($vis $Unknown: Vec,)? } @@ -2159,6 +2346,11 @@ macro_rules! impl_bounds { separator = Some(::default()); v.to_tokens(tokens); })* + $(if let Some(v) = &self.$VariantHasBody { + separator.to_tokens(tokens); + separator = Some(::default()); + v.to_tokens(tokens); + })* $(for v in &self.$Unknown { separator.to_tokens(tokens); separator = Some(::default()); @@ -2172,6 +2364,7 @@ macro_rules! impl_bounds { #[allow(non_snake_case)] $vis struct Iter { $($Variant: Option,)* + $($VariantHasBody: Option<$variant_has_body_ty>,)* $($Unknown: std::vec::IntoIter,)? } @@ -2182,6 +2375,7 @@ macro_rules! impl_bounds { fn into_iter(self) -> Self::IntoIter { Iter { $($Variant: self.$Variant,)* + $($VariantHasBody: self.$VariantHasBody,)* $($Unknown: self.$Unknown.into_iter(),)? } } @@ -2196,6 +2390,11 @@ macro_rules! impl_bounds { return Some($enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$VariantHasBody.take() { + return Some($enum_type::$VariantHasBody(value)); + } + )* $( if let Some(value) = self.$Unknown.next() { return Some($enum_type::$Unknown(value)); @@ -2211,6 +2410,11 @@ macro_rules! impl_bounds { init = f(init, $enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$VariantHasBody.take() { + init = f(init, $enum_type::$VariantHasBody(value)); + } + )* $( if let Some(value) = self.$Unknown.next() { init = f(init, $enum_type::$Unknown(value)); @@ -2227,6 +2431,9 @@ macro_rules! impl_bounds { $($enum_type::$Variant(v) => { self.$Variant = Some(v); })* + $($enum_type::$VariantHasBody(v) => { + self.$VariantHasBody = Some(v); + })* $($enum_type::$Unknown(v) => { self.$Unknown.push(v); })? @@ -2248,6 +2455,9 @@ macro_rules! impl_bounds { $(if let Some(v) = v.$Variant { self.$Variant = Some(v); })* + $(if let Some(v) = v.$VariantHasBody { + self.$VariantHasBody = Some(v); + })* $(self.$Unknown.extend(v.$Unknown);)* }); } @@ -2302,6 +2512,8 @@ impl_bounds! { Size, StaticType, Type, + #[has_body] + PhantomConstGet(PhantomConstGetBound), #[unknown] Unknown, } @@ -2317,6 +2529,8 @@ impl_bounds! { ResetType, StaticType, Type, + #[has_body] + PhantomConstGet(PhantomConstGetBound), #[unknown] Unknown, } @@ -2332,6 +2546,7 @@ impl From for ParsedBound { ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), ParsedTypeBound::Type(v) => ParsedBound::Type(v), + ParsedTypeBound::PhantomConstGet(v) => ParsedBound::PhantomConstGet(v), ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), } } @@ -2347,6 +2562,7 @@ impl From for ParsedBounds { ResetType, StaticType, Type, + PhantomConstGet, Unknown, } = value; Self { @@ -2359,6 +2575,7 @@ impl From for ParsedBounds { Size: None, StaticType, Type, + PhantomConstGet, Unknown, } } @@ -2395,6 +2612,10 @@ impl ParsedTypeBound { ParsedTypeBound::Type(known_items::Type(span)), ]), Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), + Self::PhantomConstGet(v) => ParsedTypeBounds::from_iter([ + ParsedTypeBound::from(v), + ParsedTypeBound::Type(known_items::Type(span)), + ]), Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]), } } @@ -2430,6 +2651,7 @@ impl From for ParsedBounds { Size, StaticType: None, Type: None, + PhantomConstGet: None, Unknown: vec![], } } @@ -2532,6 +2754,9 @@ 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::PhantomConstGet(v) => { + ParsedBoundCategory::Type(ParsedTypeBound::PhantomConstGet(v)) + } Self::Unknown(v) => ParsedBoundCategory::Unknown(v), } } @@ -3417,7 +3642,8 @@ impl ParsedGenerics { | ParsedTypeBound::BundleType(_) | ParsedTypeBound::EnumType(_) | ParsedTypeBound::IntType(_) - | ParsedTypeBound::ResetType(_) => { + | ParsedTypeBound::ResetType(_) + | ParsedTypeBound::PhantomConstGet(_) => { errors.error(bound, "bounds on mask types are not implemented"); } ParsedTypeBound::StaticType(bound) => { @@ -4386,3 +4612,124 @@ impl MakeHdlTypeExpr for ParsedTypeTuple { }) } } + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum ParsedSimpleVisibility { + Public(Token![pub]), + PubCrate { + pub_token: Token![pub], + paren_token: Paren, + crate_token: Token![crate], + }, + Inherited, +} + +impl From for Visibility { + fn from(value: ParsedSimpleVisibility) -> Self { + match value { + ParsedSimpleVisibility::Public(v) => Visibility::Public(v), + ParsedSimpleVisibility::PubCrate { + pub_token, + paren_token, + crate_token, + } => Visibility::Restricted(syn::VisRestricted { + pub_token, + paren_token, + in_token: None, + path: Box::new(Ident::new("crate", crate_token.span).into()), + }), + ParsedSimpleVisibility::Inherited => Visibility::Inherited, + } + } +} + +impl PartialOrd for ParsedSimpleVisibility { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ParsedSimpleVisibility { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.visibility_level().cmp(&other.visibility_level()) + } +} + +impl ParsedSimpleVisibility { + const VISIBILITY_LEVEL_INHERITED: u8 = 0; + const VISIBILITY_LEVEL_RESTRICTED: u8 = 1 + Self::VISIBILITY_LEVEL_INHERITED; + const VISIBILITY_LEVEL_PUB_CRATE: u8 = 1 + Self::VISIBILITY_LEVEL_RESTRICTED; + const VISIBILITY_LEVEL_PUB: u8 = 1 + Self::VISIBILITY_LEVEL_PUB_CRATE; + fn visibility_level(self) -> u8 { + match self { + Self::Public(_) => Self::VISIBILITY_LEVEL_PUB, + Self::PubCrate { .. } => Self::VISIBILITY_LEVEL_PUB_CRATE, + Self::Inherited => Self::VISIBILITY_LEVEL_INHERITED, + } + } + pub(crate) fn parse(vis: Visibility) -> Result { + match vis { + Visibility::Public(v) => Ok(Self::Public(v)), + Visibility::Restricted(syn::VisRestricted { + pub_token, + paren_token, + in_token: None, + path, + }) if path.is_ident("crate") => Ok(Self::PubCrate { + pub_token, + paren_token, + crate_token: Token![crate](path.get_ident().expect("just checked").span()), + }), + Visibility::Restricted(v) => Err(v), + Visibility::Inherited => Ok(Self::Inherited), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum ParsedVisibility { + Simple(ParsedSimpleVisibility), + Restricted(syn::VisRestricted), +} + +impl From for Visibility { + fn from(value: ParsedVisibility) -> Self { + match value { + ParsedVisibility::Simple(v) => v.into(), + ParsedVisibility::Restricted(v) => Visibility::Restricted(v), + } + } +} + +impl PartialOrd for ParsedVisibility { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (ParsedVisibility::Simple(l), ParsedVisibility::Simple(r)) => Some(l.cmp(r)), + (ParsedVisibility::Simple(l), ParsedVisibility::Restricted(_)) => Some( + l.visibility_level() + .cmp(&ParsedSimpleVisibility::VISIBILITY_LEVEL_RESTRICTED), + ), + (ParsedVisibility::Restricted(_), ParsedVisibility::Simple(r)) => { + Some(ParsedSimpleVisibility::VISIBILITY_LEVEL_RESTRICTED.cmp(&r.visibility_level())) + } + (ParsedVisibility::Restricted(l), ParsedVisibility::Restricted(r)) => { + (l == r).then_some(std::cmp::Ordering::Equal) + } + } + } +} + +impl ParsedVisibility { + #[allow(dead_code)] + pub(crate) fn parse(vis: Visibility) -> Self { + match ParsedSimpleVisibility::parse(vis) { + Ok(simple) => Self::Simple(simple), + Err(restricted) => Self::Restricted(restricted), + } + } + #[allow(dead_code)] + pub(crate) fn min<'a>(&'a self, other: &'a Self) -> Option<&'a Self> { + self.partial_cmp(other) + .map(|ord| if ord.is_lt() { self } else { other }) + } +} diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index def91eb4..152053cb 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -66,6 +66,7 @@ mod kw { } custom_keyword!(__evaluated_cfgs); + custom_keyword!(add_platform_io); custom_keyword!(all); custom_keyword!(any); custom_keyword!(cfg); @@ -75,6 +76,7 @@ mod kw { custom_keyword!(connect_inexact); custom_keyword!(custom_bounds); custom_keyword!(flip); + custom_keyword!(get); custom_keyword!(hdl); custom_keyword!(hdl_module); custom_keyword!(incomplete_wire); @@ -885,7 +887,13 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr } } let _print_on_panic = PrintOnPanic(&contents); - let contents = prettyplease::unparse(&parse_quote! { #contents }); + let mut parse_err = None; + let (Ok(contents) | Err(contents)) = syn::parse2(contents.clone()) + .map(|file| prettyplease::unparse(&file)) + .map_err(|e| { + parse_err = Some(e); + contents.to_string() + }); let hash = ::digest(&contents); let hash = base16ct::HexDisplay(&hash[..5]); file.write_all(contents.as_bytes()).unwrap(); @@ -897,9 +905,26 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr e.unwrap(); } } - eprintln!("generated {}", dest_file.display()); + let log_msg = if let Some(parse_err) = parse_err { + format!( + "fayalite-proc-macros-impl internal error:\nfailed to parse generated output: {parse_err}\nunformatted output is in: {}\n", + dest_file.display() + ) + } else { + format!("generated {}\n", dest_file.display()) + }; + // write message atomically if possible + let mut stderr = std::io::stderr().lock(); + let write_result = stderr.write_all(log_msg.as_bytes()); + let flush_result = stderr.flush(); + drop(stderr); // unlock before we try to panic + write_result.unwrap(); + flush_result.unwrap(); + std::io::stderr() + .lock() + .write_all(log_msg.as_bytes()) + .unwrap(); let dest_file = dest_file.to_str().unwrap(); - quote! { include!(#dest_file); } diff --git a/crates/fayalite-proc-macros-impl/src/module.rs b/crates/fayalite-proc-macros-impl/src/module.rs index c7caa160..5628ff94 100644 --- a/crates/fayalite-proc-macros-impl/src/module.rs +++ b/crates/fayalite-proc-macros-impl/src/module.rs @@ -4,7 +4,7 @@ use crate::{ Errors, HdlAttr, PairsIterExt, hdl_type_common::{ParsedGenerics, SplitForImpl}, kw, - module::transform_body::{HdlLet, HdlLetKindIO}, + module::transform_body::{HdlLet, HdlLetKindIO, ModuleIOOrAddPlatformIO}, options, }; use proc_macro2::TokenStream; @@ -39,7 +39,7 @@ pub(crate) fn check_name_conflicts_with_module_builder(name: &Ident) -> syn::Res if name == "m" { Err(Error::new_spanned( name, - "name conflicts with implicit `m: &mut ModuleBuilder<_>`", + "name conflicts with implicit `m: &ModuleBuilder`", )) } else { Ok(()) @@ -67,7 +67,7 @@ struct ModuleFnModule { vis: Visibility, sig: Signature, block: Box, - struct_generics: ParsedGenerics, + struct_generics: Option, the_struct: TokenStream, } @@ -290,7 +290,7 @@ impl ModuleFn { paren_token, body, } => { - debug_assert!(io.is_empty()); + debug_assert!(matches!(io, ModuleIOOrAddPlatformIO::ModuleIO(v) if v.is_empty())); return Ok(Self(ModuleFnImpl::Fn { attrs, config_options: HdlAttr { @@ -322,6 +322,21 @@ impl ModuleFn { body, }, }; + let io = match io { + ModuleIOOrAddPlatformIO::ModuleIO(io) => io, + ModuleIOOrAddPlatformIO::AddPlatformIO => { + return Ok(Self(ModuleFnImpl::Module(ModuleFnModule { + attrs, + config_options, + module_kind: module_kind.unwrap(), + vis, + sig, + block, + struct_generics: None, + the_struct: TokenStream::new(), + }))); + } + }; let (_struct_impl_generics, _struct_type_generics, struct_where_clause) = struct_generics.split_for_impl(); let struct_where_clause: Option = parse_quote! { #struct_where_clause }; @@ -364,7 +379,7 @@ impl ModuleFn { vis, sig, block, - struct_generics, + struct_generics: Some(struct_generics), the_struct, }))) } @@ -433,9 +448,14 @@ impl ModuleFn { ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal }, }; let fn_name = &outer_sig.ident; - let (_struct_impl_generics, struct_type_generics, _struct_where_clause) = - struct_generics.split_for_impl(); - let struct_ty = quote! {#fn_name #struct_type_generics}; + let struct_ty = match struct_generics { + Some(struct_generics) => { + let (_struct_impl_generics, struct_type_generics, _struct_where_clause) = + struct_generics.split_for_impl(); + quote! {#fn_name #struct_type_generics} + } + None => quote! {::fayalite::bundle::Bundle}, + }; body_sig.ident = parse_quote! {__body}; body_sig .inputs 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 6859f69f..7b41f5eb 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -39,6 +39,7 @@ options! { pub(crate) enum LetFnKind { Input(input), Output(output), + AddPlatformIO(add_platform_io), Instance(instance), RegBuilder(reg_builder), Wire(wire), @@ -216,6 +217,49 @@ impl HdlLetKindToTokens for HdlLetKindInstance { } } +#[derive(Clone, Debug)] +pub(crate) struct HdlLetKindAddPlatformIO { + pub(crate) m: kw::m, + pub(crate) dot_token: Token![.], + pub(crate) add_platform_io: kw::add_platform_io, + pub(crate) paren: Paren, + pub(crate) platform_io_builder: Box, +} + +impl ParseTypes for HdlLetKindAddPlatformIO { + fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result { + Ok(input.clone()) + } +} + +impl_fold! { + struct HdlLetKindAddPlatformIO<> { + m: kw::m, + dot_token: Token![.], + add_platform_io: kw::add_platform_io, + paren: Paren, + platform_io_builder: Box, + } +} + +impl HdlLetKindToTokens for HdlLetKindAddPlatformIO { + fn ty_to_tokens(&self, _tokens: &mut TokenStream) {} + + fn expr_to_tokens(&self, tokens: &mut TokenStream) { + let Self { + m, + dot_token, + add_platform_io, + paren, + platform_io_builder, + } = self; + m.to_tokens(tokens); + dot_token.to_tokens(tokens); + add_platform_io.to_tokens(tokens); + paren.surround(tokens, |tokens| platform_io_builder.to_tokens(tokens)); + } +} + #[derive(Clone, Debug)] pub(crate) struct RegBuilderClockDomain { pub(crate) dot_token: Token![.], @@ -711,6 +755,7 @@ impl HdlLetKindMemory { #[derive(Clone, Debug)] pub(crate) enum HdlLetKind { IO(HdlLetKindIO), + AddPlatformIO(HdlLetKindAddPlatformIO), Incomplete(HdlLetKindIncomplete), Instance(HdlLetKindInstance), RegBuilder(HdlLetKindRegBuilder), @@ -721,6 +766,7 @@ pub(crate) enum HdlLetKind { impl_fold! { enum HdlLetKind { IO(HdlLetKindIO), + AddPlatformIO(HdlLetKindAddPlatformIO), Incomplete(HdlLetKindIncomplete), Instance(HdlLetKindInstance), RegBuilder(HdlLetKindRegBuilder), @@ -736,6 +782,9 @@ impl, I> ParseTypes> for HdlLetKind { ) -> Result { match input { HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO), + HdlLetKind::AddPlatformIO(input) => { + ParseTypes::parse_types(input, parser).map(HdlLetKind::AddPlatformIO) + } HdlLetKind::Incomplete(input) => { ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete) } @@ -861,6 +910,23 @@ impl HdlLetKindParse for HdlLetKind { ModuleIOKind::Output(output), ) .map(Self::IO), + LetFnKind::AddPlatformIO((add_platform_io,)) => { + if let Some(parsed_ty) = parsed_ty { + return Err(Error::new_spanned( + parsed_ty.1, + "type annotation not allowed for instance", + )); + } + let (m, dot_token) = unwrap_m_dot(m_dot, kind)?; + let paren_contents; + Ok(Self::AddPlatformIO(HdlLetKindAddPlatformIO { + m, + dot_token, + add_platform_io, + paren: parenthesized!(paren_contents in input), + platform_io_builder: paren_contents.call(parse_single_fn_arg)?, + })) + } LetFnKind::Instance((instance,)) => { if let Some(parsed_ty) = parsed_ty { return Err(Error::new_spanned( @@ -936,6 +1002,7 @@ impl HdlLetKindToTokens for HdlLetKind { fn ty_to_tokens(&self, tokens: &mut TokenStream) { match self { HdlLetKind::IO(v) => v.ty_to_tokens(tokens), + HdlLetKind::AddPlatformIO(v) => v.ty_to_tokens(tokens), HdlLetKind::Incomplete(v) => v.ty_to_tokens(tokens), HdlLetKind::Instance(v) => v.ty_to_tokens(tokens), HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens), @@ -947,6 +1014,7 @@ impl HdlLetKindToTokens for HdlLetKind { fn expr_to_tokens(&self, tokens: &mut TokenStream) { match self { HdlLetKind::IO(v) => v.expr_to_tokens(tokens), + HdlLetKind::AddPlatformIO(v) => v.expr_to_tokens(tokens), HdlLetKind::Incomplete(v) => v.expr_to_tokens(tokens), HdlLetKind::Instance(v) => v.expr_to_tokens(tokens), HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens), @@ -1149,7 +1217,7 @@ impl ToTokens for ImplicitName { struct Visitor<'a> { module_kind: Option, errors: Errors, - io: Vec, + io: ModuleIOOrAddPlatformIO, block_depth: usize, parsed_generics: &'a ParsedGenerics, } @@ -1289,7 +1357,81 @@ impl Visitor<'_> { }), semi_token: hdl_let.semi_token, }; - self.io.push(hdl_let); + match &mut self.io { + ModuleIOOrAddPlatformIO::ModuleIO(io) => io.push(hdl_let), + ModuleIOOrAddPlatformIO::AddPlatformIO => { + self.errors.error( + kind, + "can't have other inputs/outputs in a module using m.add_platform_io()", + ); + } + } + let_stmt + } + fn process_hdl_let_add_platform_io( + &mut self, + hdl_let: HdlLet, + ) -> Local { + let HdlLet { + mut attrs, + hdl_attr: _, + let_token, + mut_token, + ref name, + eq_token, + kind: + HdlLetKindAddPlatformIO { + m, + dot_token, + add_platform_io, + paren, + platform_io_builder, + }, + semi_token, + } = hdl_let; + let mut expr = quote! {#m #dot_token #add_platform_io}; + paren.surround(&mut expr, |expr| { + let name_str = ImplicitName { + name, + span: name.span(), + }; + quote_spanned! {name.span()=> + #name_str, #platform_io_builder + } + .to_tokens(expr); + }); + self.require_module(add_platform_io); + attrs.push(parse_quote_spanned! {let_token.span=> + #[allow(unused_variables)] + }); + let let_stmt = Local { + attrs, + let_token, + pat: parse_quote! { #mut_token #name }, + init: Some(LocalInit { + eq_token, + expr: parse_quote! { #expr }, + diverge: None, + }), + semi_token, + }; + match &mut self.io { + ModuleIOOrAddPlatformIO::ModuleIO(io) => { + for io in io { + self.errors.error( + io.kind.kind, + "can't have other inputs/outputs in a module using m.add_platform_io()", + ); + } + } + ModuleIOOrAddPlatformIO::AddPlatformIO => { + self.errors.error( + add_platform_io, + "can't use m.add_platform_io() more than once in a single module", + ); + } + } + self.io = ModuleIOOrAddPlatformIO::AddPlatformIO; let_stmt } fn process_hdl_let_instance(&mut self, hdl_let: HdlLet) -> Local { @@ -1510,6 +1652,7 @@ impl Visitor<'_> { } the_match! { IO => process_hdl_let_io, + AddPlatformIO => process_hdl_let_add_platform_io, Incomplete => process_hdl_let_incomplete, Instance => process_hdl_let_instance, RegBuilder => process_hdl_let_reg_builder, @@ -1753,15 +1896,20 @@ impl Fold for Visitor<'_> { } } +pub(crate) enum ModuleIOOrAddPlatformIO { + ModuleIO(Vec), + AddPlatformIO, +} + pub(crate) fn transform_body( module_kind: Option, mut body: Box, parsed_generics: &ParsedGenerics, -) -> syn::Result<(Box, Vec)> { +) -> syn::Result<(Box, ModuleIOOrAddPlatformIO)> { let mut visitor = Visitor { module_kind, errors: Errors::new(), - io: vec![], + io: ModuleIOOrAddPlatformIO::ModuleIO(vec![]), block_depth: 0, parsed_generics, }; 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 1aabb19e..e30eb0fa 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 @@ -88,6 +88,9 @@ impl Visitor<'_> { field.expr = parse_quote_spanned! {field.member.span()=> ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)) }; + field + .colon_token + .get_or_insert(Token![:](field.member.span())); } return parse_quote_spanned! {name_span=> { 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 069f00d2..ca06c0b1 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 @@ -10,7 +10,7 @@ use crate::{ }; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned}; -use std::collections::BTreeSet; +use std::collections::BTreeMap; use syn::{ Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr, PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment, @@ -24,65 +24,65 @@ use syn::{ macro_rules! visit_trait { ( - $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)* + $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &mut $Value:ty) $block:block)* ) => { trait VisitMatchPat<'a> { - $(fn $fn(&mut self, $value: &'a $Value) { + $(fn $fn(&mut self, $value: &'a mut $Value) { $fn(self, $value); })* } - $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)* + $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a mut $Value) $block)* }; } visit_trait! { - fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) { - let MatchPatBinding { ident: _ } = v; + fn visit_match_pat_binding(_state: _, v: &mut MatchPatBinding) { + let MatchPatBinding { mutability: _, ident: _ } = v; } - fn visit_match_pat_wild(_state: _, v: &MatchPatWild) { + fn visit_match_pat_wild(_state: _, v: &mut MatchPatWild) { let MatchPatWild { underscore_token: _ } = v; } - fn visit_match_pat_rest(_state: _, v: &MatchPatRest) { + fn visit_match_pat_rest(_state: _, v: &mut MatchPatRest) { let MatchPatRest { dot2_token: _ } = v; } - fn visit_match_pat_paren(state: _, v: &MatchPatParen) { + fn visit_match_pat_paren(state: _, v: &mut MatchPatParen) { let MatchPatParen { paren_token: _, pat } = v; state.visit_match_pat(pat); } - fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen) { + fn visit_match_pat_paren_simple(state: _, v: &mut MatchPatParen) { let MatchPatParen { paren_token: _, pat } = v; state.visit_match_pat_simple(pat); } - fn visit_match_pat_or(state: _, v: &MatchPatOr) { + fn visit_match_pat_or(state: _, v: &mut 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) { + fn visit_match_pat_or_simple(state: _, v: &mut 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) { + fn visit_match_pat_struct_field(state: _, v: &mut MatchPatStructField) { let MatchPatStructField { field_name: _, colon_token: _, pat } = v; state.visit_match_pat_simple(pat); } - fn visit_match_pat_struct(state: _, v: &MatchPatStruct) { + fn visit_match_pat_struct(state: _, v: &mut 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) { + fn visit_match_pat_tuple(state: _, v: &mut 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) { + fn visit_match_pat_enum_variant(state: _, v: &mut MatchPatEnumVariant) { let MatchPatEnumVariant { match_span:_, sim:_, @@ -95,7 +95,7 @@ visit_trait! { state.visit_match_pat_simple(v); } } - fn visit_match_pat_simple(state: _, v: &MatchPatSimple) { + fn visit_match_pat_simple(state: _, v: &mut MatchPatSimple) { match v { MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v), MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v), @@ -104,7 +104,7 @@ visit_trait! { MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v), } } - fn visit_match_pat(state: _, v: &MatchPat) { + fn visit_match_pat(state: _, v: &mut MatchPat) { match v { MatchPat::Simple(v) => state.visit_match_pat_simple(v), MatchPat::Or(v) => state.visit_match_pat_or(v), @@ -118,13 +118,15 @@ visit_trait! { with_debug_clone_and_fold! { struct MatchPatBinding<> { + mutability: Option, ident: Ident, } } impl ToTokens for MatchPatBinding { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { ident } = self; + let Self { mutability, ident } = self; + mutability.to_tokens(tokens); ident.to_tokens(tokens); } } @@ -211,12 +213,20 @@ impl ToTokens for MatchPatStructField { colon_token, pat, } = self; - field_name.to_tokens(tokens); - if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) { + if let ( + None, + MatchPatSimple::Binding(MatchPatBinding { + mutability: _, + ident, + }), + ) = (colon_token, pat) + { if field_name == ident { + pat.to_tokens(tokens); return; } } + field_name.to_tokens(tokens); colon_token .unwrap_or_else(|| Token![:](field_name.span())) .to_tokens(tokens); @@ -450,7 +460,7 @@ trait ParseMatchPat: Sized { Pat::Ident(PatIdent { attrs: _, by_ref, - mutability, + mut mutability, ident, subpat, }) => { @@ -459,10 +469,13 @@ trait ParseMatchPat: Sized { .errors .error(by_ref, "ref not allowed in #[hdl] patterns"); } - if let Some(mutability) = mutability { - state - .errors - .error(mutability, "mut not allowed in #[hdl] patterns"); + if let Some(mut_token) = mutability { + if state.sim.is_none() { + state + .errors + .error(mut_token, "mut not allowed in #[hdl] patterns"); + mutability = None; // avoid duplicate errors + } } if let Some((at_token, _)) = subpat { state @@ -474,18 +487,26 @@ trait ParseMatchPat: Sized { variant_path, enum_path, variant_name, - }) => Self::enum_variant( - state, - MatchPatEnumVariant { - match_span: state.match_span, - sim: state.sim, - variant_path, - enum_path, - variant_name, - field: None, - }, - ), + }) => { + if let Some(mut_token) = mutability { + state + .errors + .error(mut_token, "mut not allowed on unit variants"); + } + Self::enum_variant( + state, + MatchPatEnumVariant { + match_span: state.match_span, + sim: state.sim, + variant_path, + enum_path, + variant_name, + field: None, + }, + ) + } Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding { + mutability, ident, }))), } @@ -980,15 +1001,16 @@ struct HdlMatchParseState<'a> { struct HdlLetPatVisitState<'a> { errors: &'a mut Errors, - bindings: BTreeSet<&'a Ident>, + bindings: BTreeMap, } 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_binding(&mut self, v: &'a mut MatchPatBinding) { + self.bindings.insert(v.ident.clone(), v.clone()); + v.mutability = None; } - fn visit_match_pat_or(&mut self, v: &'a MatchPatOr) { + fn visit_match_pat_or(&mut self, v: &'a mut MatchPatOr) { if let Some(first_inner_vert) = v.first_inner_vert() { self.errors.error( first_inner_vert, @@ -998,7 +1020,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { visit_match_pat_or(self, v); } - fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr) { + fn visit_match_pat_or_simple(&mut self, v: &'a mut MatchPatOr) { if let Some(first_inner_vert) = v.first_inner_vert() { self.errors.error( first_inner_vert, @@ -1008,7 +1030,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { visit_match_pat_or_simple(self, v); } - fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) { + fn visit_match_pat_enum_variant(&mut self, v: &'a mut MatchPatEnumVariant) { self.errors.error(v, "refutable pattern in let statement"); } } @@ -1048,7 +1070,7 @@ impl Visitor<'_> { .error(else_, "#[hdl] let ... else { ... } is not implemented"); return empty_let(); } - let Ok(pat) = MatchPat::parse( + let Ok(mut pat) = MatchPat::parse( &mut HdlMatchParseState { sim, match_span: span, @@ -1060,20 +1082,27 @@ impl Visitor<'_> { }; let mut state = HdlLetPatVisitState { errors: &mut self.errors, - bindings: BTreeSet::new(), + bindings: BTreeMap::new(), }; - state.visit_match_pat(&pat); + state.visit_match_pat(&mut pat); let HdlLetPatVisitState { errors: _, bindings, } = state; + let bindings_idents = bindings.keys(); + let bindings = bindings.values(); 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,)*) + let __match_value = #expr; + let __match_value = { + use ::fayalite::sim::value::match_sim_value::*; + // use method syntax to deduce the correct trait to call + ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value() + }; + #let_token #pat #eq_token __match_value #semi_token + (#(#bindings_idents,)*) }; } } else { @@ -1105,7 +1134,7 @@ impl Visitor<'_> { __match_variant, ); #let_token #pat #eq_token __match_variant #semi_token - (#(#bindings,)* __scope,) + (#(#bindings_idents,)* __scope,) }; } }; @@ -1142,8 +1171,13 @@ impl Visitor<'_> { 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) { + let __match_value = #expr; + let __match_value = { + use ::fayalite::sim::value::match_sim_value::*; + // use method syntax to deduce the correct trait to call + ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value() + }; + #match_token __match_value { #(#arms)* } } diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 082e6079..fdf1c871 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -14,9 +14,11 @@ rust-version.workspace = true version.workspace = true [dependencies] +base64.workspace = true bitvec.workspace = true blake3.workspace = true clap.workspace = true +clap_complete.workspace = true ctor.workspace = true eyre.workspace = true fayalite-proc-macros.workspace = true @@ -24,7 +26,7 @@ hashbrown.workspace = true jobslot.workspace = true num-bigint.workspace = true num-traits.workspace = true -os_pipe.workspace = true +ordered-float.workspace = true petgraph.workspace = true serde_json.workspace = true serde.workspace = true diff --git a/crates/fayalite/examples/blinky.rs b/crates/fayalite/examples/blinky.rs index 87b77c12..d2cdb336 100644 --- a/crates/fayalite/examples/blinky.rs +++ b/crates/fayalite/examples/blinky.rs @@ -1,47 +1,64 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use clap::Parser; -use fayalite::{cli, prelude::*}; +use fayalite::prelude::*; #[hdl_module] -fn blinky(clock_frequency: u64) { - #[hdl] - let clk: Clock = m.input(); - #[hdl] - let rst: SyncReset = m.input(); +fn blinky(platform_io_builder: PlatformIOBuilder<'_>) { + let clk_input = + platform_io_builder.peripherals_with_type::()[0].use_peripheral(); + let rst = platform_io_builder.peripherals_with_type::()[0].use_peripheral(); let cd = #[hdl] ClockDomain { - clk, - rst: rst.to_reset(), + clk: clk_input.clk, + rst, }; - let max_value = clock_frequency / 2 - 1; + let max_value = (clk_input.ty().frequency() / 2.0).round_ties_even() as u64 - 1; let int_ty = UInt::range_inclusive(0..=max_value); #[hdl] let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty)); #[hdl] let output_reg: Bool = reg_builder().clock_domain(cd).reset(false); #[hdl] + let rgb_output_reg = reg_builder().clock_domain(cd).reset( + #[hdl] + peripherals::RgbLed { + r: false, + g: false, + b: false, + }, + ); + #[hdl] if counter_reg.cmp_eq(max_value) { connect_any(counter_reg, 0u8); connect(output_reg, !output_reg); + connect(rgb_output_reg.r, !rgb_output_reg.r); + #[hdl] + if rgb_output_reg.r { + connect(rgb_output_reg.g, !rgb_output_reg.g); + #[hdl] + if rgb_output_reg.g { + connect(rgb_output_reg.b, !rgb_output_reg.b); + } + } } else { connect_any(counter_reg, counter_reg + 1_hdl_u1); } + for led in platform_io_builder.peripherals_with_type::() { + if let Ok(led) = led.try_use_peripheral() { + connect(led.on, output_reg); + } + } + for rgb_led in platform_io_builder.peripherals_with_type::() { + if let Ok(rgb_led) = rgb_led.try_use_peripheral() { + connect(rgb_led, rgb_output_reg); + } + } #[hdl] - let led: Bool = m.output(); - connect(led, output_reg); + let io = m.add_platform_io(platform_io_builder); } -#[derive(Parser)] -struct Cli { - /// clock frequency in hertz - #[arg(long, default_value = "1000000", value_parser = clap::value_parser!(u64).range(2..))] - clock_frequency: u64, - #[command(subcommand)] - cli: cli::Cli, -} - -fn main() -> cli::Result { - let cli = Cli::parse(); - cli.cli.run(blinky(cli.clock_frequency)) +fn main() { + ::main("blinky", |_, platform, _| { + Ok(JobParams::new(platform.wrap_main_module(blinky))) + }); } diff --git a/crates/fayalite/examples/tx_only_uart.rs b/crates/fayalite/examples/tx_only_uart.rs new file mode 100644 index 00000000..59e29682 --- /dev/null +++ b/crates/fayalite/examples/tx_only_uart.rs @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +use clap::builder::TypedValueParser; +use fayalite::{ + build::{ToArgs, WriteArgs}, + platform::PeripheralRef, + prelude::*, +}; +use ordered_float::NotNan; + +fn pick_clock<'a>( + platform_io_builder: &PlatformIOBuilder<'a>, +) -> PeripheralRef<'a, peripherals::ClockInput> { + let mut clks = platform_io_builder.peripherals_with_type::(); + clks.sort_by_key(|clk| { + // sort clocks by preference, smaller return values means higher preference + let mut frequency = clk.ty().frequency(); + let priority; + if frequency < 10e6 { + frequency = -frequency; // prefer bigger frequencies + priority = 1; + } else if frequency > 50e6 { + // prefer smaller frequencies + priority = 2; // least preferred + } else { + priority = 0; // most preferred + frequency = (frequency - 25e6).abs(); // prefer closer to 25MHz + } + (priority, NotNan::new(frequency).expect("should be valid")) + }); + clks[0] +} + +#[hdl_module] +fn tx_only_uart( + platform_io_builder: PlatformIOBuilder<'_>, + divisor: f64, + message: impl AsRef<[u8]>, +) { + let message = message.as_ref(); + let clk_input = pick_clock(&platform_io_builder).use_peripheral(); + let rst = platform_io_builder.peripherals_with_type::()[0].use_peripheral(); + let cd = #[hdl] + ClockDomain { + clk: clk_input.clk, + rst, + }; + let numerator = 1u128 << 16; + let denominator = (divisor * numerator as f64).round() as u128; + + #[hdl] + let remainder_reg: UInt<128> = reg_builder().clock_domain(cd).reset(0u128); + + #[hdl] + let sum: UInt<128> = wire(); + connect_any(sum, remainder_reg + numerator); + + #[hdl] + let tick_reg = reg_builder().clock_domain(cd).reset(false); + connect(tick_reg, false); + + #[hdl] + let next_remainder: UInt<128> = wire(); + connect(remainder_reg, next_remainder); + + #[hdl] + if sum.cmp_ge(denominator) { + connect_any(next_remainder, sum - denominator); + connect(tick_reg, true); + } else { + connect(next_remainder, sum); + } + + #[hdl] + let uart_state_reg = reg_builder().clock_domain(cd).reset(0_hdl_u4); + #[hdl] + let next_uart_state: UInt<4> = wire(); + + connect_any(next_uart_state, uart_state_reg + 1u8); + + #[hdl] + let message_mem: Array> = wire(Array[UInt::new_static()][message.len()]); + for (message, message_mem) in message.iter().zip(message_mem) { + connect(message_mem, *message); + } + #[hdl] + let addr_reg: UInt<32> = reg_builder().clock_domain(cd).reset(0u32); + #[hdl] + let next_addr: UInt<32> = wire(); + connect(next_addr, addr_reg); + + #[hdl] + let tx = reg_builder().clock_domain(cd).reset(true); + + #[hdl] + let tx_bits: Array = wire(); + + connect(tx_bits[0], false); // start bit + connect(tx_bits[9], true); // stop bit + + for i in 0..8 { + connect(tx_bits[i + 1], message_mem[addr_reg][i]); // data bits + } + + connect(tx, tx_bits[uart_state_reg]); + + #[hdl] + if uart_state_reg.cmp_eq(tx_bits.ty().len() - 1) { + connect(next_uart_state, 0_hdl_u4); + let next_addr_val = addr_reg + 1u8; + #[hdl] + if next_addr_val.cmp_lt(message.len()) { + connect_any(next_addr, next_addr_val); + } else { + connect(next_addr, 0u32); + } + } + + #[hdl] + if tick_reg { + connect(uart_state_reg, next_uart_state); + connect(addr_reg, next_addr); + } + + for uart in platform_io_builder.peripherals_with_type::() { + connect(uart.use_peripheral().tx, tx); + } + + #[hdl] + let io = m.add_platform_io(platform_io_builder); +} + +fn parse_baud_rate( + v: impl AsRef, +) -> Result, Box> { + let retval: NotNan = v + .as_ref() + .parse() + .map_err(|_| "invalid baud rate, must be a finite positive floating-point value")?; + if *retval > 0.0 && retval.is_finite() { + Ok(retval) + } else { + Err("baud rate must be finite and positive".into()) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] +pub struct ExtraArgs { + #[arg(long, value_parser = clap::builder::StringValueParser::new().try_map(parse_baud_rate), default_value = "115200")] + pub baud_rate: NotNan, + #[arg(long, default_value = "Hello World from Fayalite!!!\r\n", value_parser = clap::builder::NonEmptyStringValueParser::new())] + pub message: String, +} + +impl ToArgs for ExtraArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { baud_rate, message } = self; + args.write_display_arg(format_args!("--baud-rate={baud_rate}")); + args.write_long_option_eq("message", message); + } +} + +fn main() { + type Cli = BuildCli; + Cli::main( + "tx_only_uart", + |_, platform, ExtraArgs { baud_rate, message }| { + Ok(JobParams::new(platform.try_wrap_main_module(|io| { + let clk = pick_clock(&io).ty(); + let divisor = clk.frequency() / *baud_rate; + let baud_rate_error = |msg| { + ::command() + .error(clap::error::ErrorKind::ValueValidation, msg) + }; + const HUGE_DIVISOR: f64 = u64::MAX as f64; + match divisor { + divisor if !divisor.is_finite() => { + return Err(baud_rate_error("bad baud rate")); + } + HUGE_DIVISOR.. => return Err(baud_rate_error("baud rate is too small")), + 4.0.. => {} + _ => return Err(baud_rate_error("baud rate is too large")), + } + Ok(tx_only_uart(io, divisor, message)) + })?)) + }, + ); +} diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs index 1fc4705c..8d70d21c 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs @@ -31,3 +31,81 @@ //! } //! } //! ``` +//! +//! You can also use `#[hdl(sim)] let` to destructure [`SimValue`]s (or anything that implements [`ToSimValue`]). +//! +//! [`SimValue`]: crate::sim::value::SimValue +//! [`ToSimValue`]: crate::sim::value::ToSimValue +//! +//! ``` +//! # use fayalite::prelude::*; +//! #[hdl] +//! struct MyStruct { +//! a: UInt<8>, +//! b: Bool, +//! c: T, +//! } +//! +//! #[hdl] +//! fn destructure(v: SimValue>) { +//! #[hdl(sim)] +//! let MyStruct:: { +//! a, +//! mut b, +//! c, +//! } = v; +//! +//! // that gives these types: +//! let _: SimValue> = a; +//! let _: SimValue = b; +//! let _: SimValue = c; +//! *b = false; // can modify b since mut was used +//! } +//! +//! #[hdl] +//! fn destructure_ref<'a, T: Type>(v: &'a SimValue>) { +//! #[hdl(sim)] +//! let MyStruct:: { +//! a, +//! b, +//! c, +//! } = v; +//! +//! // that gives these types: +//! let _: &'a SimValue> = a; +//! let _: &'a SimValue = b; +//! let _: &'a SimValue = c; +//! } +//! +//! #[hdl] +//! fn destructure_mut<'a, T: Type>(v: &'a mut SimValue>) { +//! #[hdl(sim)] +//! let MyStruct:: { +//! a, +//! b, +//! c, +//! } = v; +//! +//! **b = true; // you can modify v by modifying b which borrows from it +//! +//! // that gives these types: +//! let _: &'a mut SimValue> = a; +//! let _: &'a mut SimValue = b; +//! let _: &'a mut SimValue = c; +//! } +//! +//! #[hdl] +//! fn destructure_to_sim_value<'a, T: Type>(v: impl ToSimValue>) { +//! #[hdl(sim)] +//! let MyStruct:: { +//! a, +//! b, +//! c, +//! } = v; +//! +//! // that gives these types: +//! let _: SimValue> = a; +//! let _: SimValue = b; +//! let _: SimValue = c; +//! } +//! ``` 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 6df70f1a..accd3d78 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 @@ -9,3 +9,78 @@ //! //! `#[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(_))`. +//! +//! You can also use `#[hdl(sim)] match` to match [`SimValue`]s (or anything that implements [`ToSimValue`]). +//! +//! `#[hdl(sim)] match` statements' bodies may have any type, unlike `#[hdl] match`. +//! +//! [`SimValue`]: crate::sim::value::SimValue +//! [`ToSimValue`]: crate::sim::value::ToSimValue +//! +//! ``` +//! # use fayalite::prelude::*; +//! #[hdl] +//! enum MyEnum { +//! A, +//! B(Bool), +//! C(T), +//! } +//! +//! #[hdl] +//! fn match_move(v: SimValue>) -> String { +//! #[hdl(sim)] +//! match v { +//! MyEnum::::A => String::from("got A"), +//! MyEnum::::B(mut b) => { +//! let _: SimValue = b; // b has this type +//! let text = format!("got B({b})"); +//! *b = true; // can modify b since mut was used +//! text +//! } +//! _ => String::from("something else"), +//! } +//! } +//! +//! #[hdl] +//! fn match_ref<'a, T: Type>(v: &'a SimValue>) -> u32 { +//! #[hdl(sim)] +//! match v { +//! MyEnum::::A => 1, +//! MyEnum::::B(b) => { +//! let _: &'a SimValue = b; // b has this type +//! println!("got B({b})"); +//! 5 +//! } +//! _ => 42, +//! } +//! } +//! +//! #[hdl] +//! fn match_mut<'a, T: Type>(v: &'a mut SimValue>) -> Option<&'a mut SimValue> { +//! #[hdl(sim)] +//! match v { +//! MyEnum::::A => None, +//! MyEnum::::B(b) => { +//! println!("got B({b})"); +//! **b = true; // you can modify v by modifying b which borrows from it +//! let _: &'a mut SimValue = b; // b has this type +//! None +//! } +//! MyEnum::::C(v) => Some(v), // you can return matched values +//! _ => None, // HDL enums can have invalid discriminants, so we need this extra match arm +//! } +//! } +//! +//! #[hdl] +//! fn match_to_sim_value<'a, T: Type>(v: impl ToSimValue>) { +//! #[hdl(sim)] +//! match v { +//! MyEnum::::A => println!("got A"), +//! MyEnum::::B(b) => { +//! let _: SimValue = b; // b has this type +//! println!("got B({b})"); +//! } +//! _ => println!("something else"), +//! } +//! } +//! ``` diff --git a/crates/fayalite/src/annotations.rs b/crates/fayalite/src/annotations.rs index 70f0460e..4ca84dd1 100644 --- a/crates/fayalite/src/annotations.rs +++ b/crates/fayalite/src/annotations.rs @@ -145,52 +145,73 @@ pub struct DocStringAnnotation { macro_rules! make_annotation_enum { ( + #[$non_exhaustive:ident] $(#[$meta:meta])* - $vis:vis enum $Annotation:ident { - $($Variant:ident($T:ident),)* + $vis:vis enum $AnnotationEnum:ident { + $($Variant:ident($T:ty),)* } ) => { + crate::annotations::make_annotation_enum!(@require_non_exhaustive $non_exhaustive); + + #[$non_exhaustive] $(#[$meta])* - $vis enum $Annotation { + #[derive(Clone, PartialEq, Eq, Hash)] + $vis enum $AnnotationEnum { $($Variant($T),)* } - $(impl IntoAnnotations for $T { - type IntoAnnotations = [$Annotation; 1]; - - fn into_annotations(self) -> Self::IntoAnnotations { - [$Annotation::$Variant(self)] + impl std::fmt::Debug for $AnnotationEnum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + $(Self::$Variant(v) => v.fmt(f),)* + } } } - impl IntoAnnotations for &'_ $T { - type IntoAnnotations = [$Annotation; 1]; - - fn into_annotations(self) -> Self::IntoAnnotations { - [$Annotation::$Variant(*self)] + $(impl From<$T> for crate::annotations::Annotation { + fn from(v: $T) -> Self { + $AnnotationEnum::$Variant(v).into() } } - impl IntoAnnotations for &'_ mut $T { - type IntoAnnotations = [$Annotation; 1]; + impl crate::annotations::IntoAnnotations for $T { + type IntoAnnotations = [crate::annotations::Annotation; 1]; fn into_annotations(self) -> Self::IntoAnnotations { - [$Annotation::$Variant(*self)] + [self.into()] } } - impl IntoAnnotations for Box<$T> { - type IntoAnnotations = [$Annotation; 1]; + impl crate::annotations::IntoAnnotations for &'_ $T { + type IntoAnnotations = [crate::annotations::Annotation; 1]; fn into_annotations(self) -> Self::IntoAnnotations { - [$Annotation::$Variant(*self)] + [crate::annotations::Annotation::from(self.clone())] + } + } + + impl crate::annotations::IntoAnnotations for &'_ mut $T { + type IntoAnnotations = [crate::annotations::Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [crate::annotations::Annotation::from(self.clone())] + } + } + + impl crate::annotations::IntoAnnotations for Box<$T> { + type IntoAnnotations = [crate::annotations::Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [crate::annotations::Annotation::from(*self)] } })* }; + (@require_non_exhaustive non_exhaustive) => {}; } +pub(crate) use make_annotation_enum; + make_annotation_enum! { - #[derive(Clone, PartialEq, Eq, Hash, Debug)] #[non_exhaustive] pub enum Annotation { DontTouch(DontTouchAnnotation), @@ -199,6 +220,7 @@ make_annotation_enum! { BlackBoxPath(BlackBoxPathAnnotation), DocString(DocStringAnnotation), CustomFirrtl(CustomFirrtlAnnotation), + Xilinx(crate::vendor::xilinx::XilinxAnnotation), } } diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 569f2e28..4e2b2235 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -3,13 +3,13 @@ use crate::{ expr::{ - CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, - ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, + CastToBits, Expr, HdlPartialEq, HdlPartialEqImpl, ReduceBits, ToExpr, ValueType, Valueless, + ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator}, }, int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType}, intern::{Intern, Interned, LazyInterned}, module::transform::visit::{Fold, Folder, Visit, Visitor}, - sim::value::{SimValue, SimValuePartialEq}, + sim::value::SimValue, source_location::SourceLocation, ty::{ CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter, @@ -19,7 +19,7 @@ use crate::{ util::ConstUsize, }; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; -use std::{iter::FusedIterator, ops::Index}; +use std::{borrow::Cow, iter::FusedIterator, ops::Index}; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ArrayType { @@ -221,7 +221,7 @@ impl Type for ArrayType { 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); + assert_eq!(element_value.ty(), element_ty); let (element_opaque, rest) = opaque.split_at(element_size); SimValue::opaque_mut(element_value).clone_from_slice(element_opaque); opaque = rest; @@ -238,7 +238,7 @@ impl Type for ArrayType { 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); + assert_eq!(element_value.ty(), element_ty); writer.fill_prefix_with(element_size, |writer| { writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice()) }); @@ -326,14 +326,30 @@ impl Index for ArrayWithoutLen { } } -impl ExprPartialEq> for ArrayType +impl HdlPartialEqImpl> for ArrayType where - Lhs: ExprPartialEq, + Lhs: HdlPartialEqImpl, { - 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()); + fn cmp_value_eq( + lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + rhs: ArrayType, + rhs_value: Cow<'_, as Type>::SimValue>, + ) -> bool { + assert_eq!(lhs.len(), rhs.len()); + let lhs = lhs.element(); + let rhs = rhs.element(); + let lhs_value: &[_] = (*lhs_value).as_ref(); + let rhs_value: &[_] = (*rhs_value).as_ref(); + for (lhs_value, rhs_value) in lhs_value.iter().zip(rhs_value) { + if !Lhs::cmp_value_eq(lhs, Cow::Borrowed(lhs_value), rhs, Cow::Borrowed(rhs_value)) { + return false; + } + } + true + } + fn cmp_expr_eq(lhs: Expr, rhs: Expr>) -> Expr { + assert_eq!(lhs.ty().len(), rhs.ty().len()); lhs.into_iter() .zip(rhs) .map(|(l, r)| l.cmp_eq(r)) @@ -341,11 +357,8 @@ where .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()); + fn cmp_expr_ne(lhs: Expr, rhs: Expr>) -> Expr { + assert_eq!(lhs.ty().len(), rhs.ty().len()); lhs.into_iter() .zip(rhs) .map(|(l, r)| l.cmp_ne(r)) @@ -353,17 +366,19 @@ where .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)) + fn cmp_valueless_eq( + lhs: Valueless, + rhs: Valueless>, + ) -> Valueless { + assert_eq!(lhs.ty().len(), rhs.ty().len()); + Valueless::new(Bool) + } + fn cmp_valueless_ne( + lhs: Valueless, + rhs: Valueless>, + ) -> Valueless { + assert_eq!(lhs.ty().len(), rhs.ty().len()); + Valueless::new(Bool) } } @@ -374,7 +389,7 @@ impl ExprIntoIterator for ArrayType { fn expr_into_iter(e: Expr) -> Self::ExprIntoIter { ExprArrayIter { base: e, - indexes: 0..Expr::ty(e).len(), + indexes: 0..e.ty().len(), } } } diff --git a/crates/fayalite/src/build.rs b/crates/fayalite/src/build.rs new file mode 100644 index 00000000..a9e9635c --- /dev/null +++ b/crates/fayalite/src/build.rs @@ -0,0 +1,2803 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + build::graph::JobGraph, + bundle::{Bundle, BundleType}, + intern::{Intern, InternSlice, Interned}, + module::Module, + platform::{DynPlatform, Platform}, + util::{job_server::AcquiredJob, os_str_strip_prefix}, + vendor, +}; +use clap::ArgAction; +use serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{DeserializeOwned, Error as _}, + ser::Error as _, +}; +use std::{ + any::{Any, TypeId}, + borrow::Cow, + cmp::Ordering, + ffi::{OsStr, OsString}, + fmt, + hash::{Hash, Hasher}, + io::Write, + marker::PhantomData, + path::{Path, PathBuf}, + sync::{Arc, OnceLock}, +}; +use tempfile::TempDir; + +pub mod external; +pub mod firrtl; +pub mod formal; +pub mod graph; +pub mod registry; +pub mod verilog; + +pub(crate) fn built_in_job_kinds() -> impl IntoIterator { + [DynJobKind::new(BaseJobKind)] + .into_iter() + .chain(firrtl::built_in_job_kinds()) + .chain(formal::built_in_job_kinds()) + .chain(vendor::built_in_job_kinds()) + .chain(verilog::built_in_job_kinds()) +} + +#[derive(Clone, Hash, PartialEq, Eq, Debug)] +#[non_exhaustive] +pub enum JobItem { + Path { + path: Interned, + }, + DynamicPaths { + paths: Vec>, + source_job_name: Interned, + }, +} + +impl JobItem { + pub fn name(&self) -> JobItemName { + match self { + &JobItem::Path { path } => JobItemName::Path { path }, + &JobItem::DynamicPaths { + paths: _, + source_job_name, + } => JobItemName::DynamicPaths { source_job_name }, + } + } +} + +#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum JobItemName { + Path { path: Interned }, + DynamicPaths { source_job_name: Interned }, +} + +impl JobItemName { + fn as_ref(&self) -> JobItemNameRef<'_> { + match self { + JobItemName::Path { path } => JobItemNameRef::Path { path }, + JobItemName::DynamicPaths { source_job_name } => { + JobItemNameRef::DynamicPaths { source_job_name } + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum JobItemNameRef<'a> { + Path { path: &'a Path }, + DynamicPaths { source_job_name: &'a str }, +} + +/// ordered by string contents, not by `Interned` +impl PartialOrd for JobItemName { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// ordered by string contents, not by `Interned` +impl Ord for JobItemName { + fn cmp(&self, other: &Self) -> Ordering { + if self == other { + Ordering::Equal + } else { + self.as_ref().cmp(&other.as_ref()) + } + } +} + +pub trait WriteArgs: + for<'a> Extend<&'a str> + + for<'a> Extend<&'a OsStr> + + for<'a> Extend<&'a Path> + + for<'a> Extend> + + for<'a> Extend> + + for<'a> Extend> + + Extend + + Extend + + Extend + + Extend> + + Extend> + + Extend> +{ + fn write_display_args(&mut self, args: impl IntoIterator) { + self.extend(args.into_iter().map(|v| v.to_string())); + } + fn write_owned_args(&mut self, args: impl IntoIterator>) { + self.extend(args.into_iter().map(Into::::into)) + } + fn write_args<'a>(&mut self, args: impl IntoIterator>); + fn write_interned_args(&mut self, args: impl IntoIterator>>) { + self.extend(args.into_iter().map(Into::>::into)) + } + fn write_display_arg(&mut self, arg: impl fmt::Display) { + self.write_display_args([arg]); + } + fn write_owned_arg(&mut self, arg: impl Into) { + self.extend([arg.into()]); + } + fn write_arg(&mut self, arg: impl AsRef) { + self.extend([arg.as_ref()]); + } + /// writes `--{name}={value}` + fn write_long_option_eq(&mut self, name: impl AsRef, value: impl AsRef) { + let name = name.as_ref(); + let value = value.as_ref(); + let mut option = + OsString::with_capacity(name.len().saturating_add(value.len()).saturating_add(3)); + option.push("--"); + option.push(name); + option.push("="); + option.push(value); + self.write_owned_arg(option); + } + fn write_interned_arg(&mut self, arg: impl Into>) { + self.extend([arg.into()]); + } + /// finds the first option that is `--{option_name}={value}` and returns `value` + fn get_long_option_eq(&self, option_name: impl AsRef) -> Option<&OsStr>; +} + +pub trait ArgsWriterArg: + AsRef + + From> + + for<'a> From> + + for<'a> From<&'a OsStr> + + From +{ +} + +impl ArgsWriterArg for Interned {} + +impl ArgsWriterArg for OsString {} + +pub struct ArgsWriter(pub Vec); + +impl Default for ArgsWriter { + fn default() -> Self { + Self(Default::default()) + } +} + +impl ArgsWriter { + fn get_long_option_eq_helper(&self, option_name: &str) -> Option<&OsStr> { + self.0.iter().find_map(|arg| { + os_str_strip_prefix(arg.as_ref(), "--") + .and_then(|arg| os_str_strip_prefix(arg, option_name)) + .and_then(|arg| os_str_strip_prefix(arg, "=")) + }) + } +} + +impl<'a, A: ArgsWriterArg> Extend<&'a str> for ArgsWriter { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(AsRef::::as_ref)) + } +} + +impl<'a, A: ArgsWriterArg> Extend<&'a OsStr> for ArgsWriter { + fn extend>(&mut self, iter: T) { + self.0.extend(iter.into_iter().map(Into::into)) + } +} + +impl<'a, A: ArgsWriterArg> Extend<&'a Path> for ArgsWriter { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(AsRef::::as_ref)) + } +} + +impl Extend for ArgsWriter { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(OsString::from)) + } +} + +impl Extend for ArgsWriter { + fn extend>(&mut self, iter: T) { + self.0.extend(iter.into_iter().map(Into::into)) + } +} + +impl Extend for ArgsWriter { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(OsString::from)) + } +} + +impl Extend> for ArgsWriter { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().map(Interned::::from)) + } +} + +impl Extend> for ArgsWriter { + fn extend>>(&mut self, iter: T) { + self.0.extend(iter.into_iter().map(Into::into)) + } +} + +impl Extend> for ArgsWriter { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().map(Interned::::from)) + } +} + +impl<'a, A: ArgsWriterArg> Extend> for ArgsWriter { + fn extend>>(&mut self, iter: T) { + self.0.extend(iter.into_iter().map(|v| { + match v { + Cow::Borrowed(v) => Cow::::Borrowed(v.as_ref()), + Cow::Owned(v) => Cow::Owned(v.into()), + } + .into() + })) + } +} + +impl<'a, A: ArgsWriterArg> Extend> for ArgsWriter { + fn extend>>(&mut self, iter: T) { + self.0.extend(iter.into_iter().map(Into::into)) + } +} + +impl<'a, A: ArgsWriterArg> Extend> for ArgsWriter { + fn extend>>(&mut self, iter: T) { + self.0.extend(iter.into_iter().map(|v| { + match v { + Cow::Borrowed(v) => Cow::::Borrowed(v.as_ref()), + Cow::Owned(v) => Cow::Owned(v.into()), + } + .into() + })) + } +} + +impl WriteArgs for ArgsWriter { + fn write_args<'a>(&mut self, args: impl IntoIterator>) { + self.0.extend(args.into_iter().map(|v| v.as_ref().into())) + } + fn get_long_option_eq(&self, option_name: impl AsRef) -> Option<&OsStr> { + self.get_long_option_eq_helper(option_name.as_ref()) + } +} + +pub trait ToArgs: clap::Args + 'static + Send + Sync + Hash + Eq + fmt::Debug + Clone { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)); + fn to_interned_args(&self) -> Interned<[Interned]> { + Intern::intern_owned(self.to_interned_args_vec()) + } + fn to_interned_args_vec(&self) -> Vec> { + let mut retval = ArgsWriter::default(); + self.to_args(&mut retval); + retval.0 + } + fn to_os_string_args(&self) -> Vec { + let mut retval = ArgsWriter::default(); + self.to_args(&mut retval); + retval.0 + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct JobKindAndArgs { + pub kind: K, + pub args: K::Args, +} + +impl JobKindAndArgs { + pub fn args_to_jobs( + self, + dependencies: ::KindsAndArgs, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result> { + K::args_to_jobs( + JobArgsAndDependencies { + args: self, + dependencies, + }, + params, + global_params, + ) + } +} + +impl> Copy for JobKindAndArgs {} + +impl From> for DynJobArgs { + fn from(value: JobKindAndArgs) -> Self { + let JobKindAndArgs { kind, args } = value; + DynJobArgs::new(kind, args) + } +} + +impl TryFrom for JobKindAndArgs { + type Error = DynJobArgs; + fn try_from(value: DynJobArgs) -> Result { + value.downcast() + } +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct JobAndKind { + pub kind: K, + pub job: K::Job, +} + +impl> Clone for JobAndKind { + fn clone(&self) -> Self { + Self { + kind: self.kind.clone(), + job: self.job.clone(), + } + } +} + +impl> Copy for JobAndKind {} + +impl From> for DynJob { + fn from(value: JobAndKind) -> Self { + let JobAndKind { kind, job } = value; + DynJob::new(kind, job) + } +} + +impl> TryFrom for JobAndKind { + type Error = DynJob; + fn try_from(value: DynJob) -> Result { + value.downcast() + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct JobKindAndDependencies { + pub kind: K, + pub dependencies: K::Dependencies, +} + +impl Default for JobKindAndDependencies { + fn default() -> Self { + Self::new(K::default()) + } +} + +impl JobKindAndDependencies { + pub fn new(kind: K) -> Self { + Self { + kind, + dependencies: kind.dependencies(), + } + } +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct JobAndDependencies { + pub job: JobAndKind, + pub dependencies: ::JobsAndKinds, +} + +impl JobAndDependencies { + pub fn get_job(&self) -> &J + where + Self: GetJob, + { + GetJob::get_job(self) + } + pub fn base_job(&self) -> &BaseJob { + self.job.kind.base_job(&self.job.job, &self.dependencies) + } +} + +impl Clone for JobAndDependencies +where + K::Job: Clone, + ::JobsAndKinds: Clone, +{ + fn clone(&self) -> Self { + Self { + job: self.job.clone(), + dependencies: self.dependencies.clone(), + } + } +} + +impl Copy for JobAndDependencies +where + K::Job: Copy, + ::JobsAndKinds: Copy, +{ +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct JobArgsAndDependencies { + pub args: JobKindAndArgs, + pub dependencies: ::KindsAndArgs, +} + +impl Copy for JobArgsAndDependencies +where + K::Args: Copy, + ::KindsAndArgs: Copy, +{ +} + +impl JobArgsAndDependencies { + pub fn args_to_jobs( + self, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result> { + K::args_to_jobs(self, params, global_params) + } + pub fn base_job_args(&self) -> &BaseJobArgs { + self.args + .kind + .base_job_args(&self.args.args, &self.dependencies) + } +} + +impl>, D: JobKind> JobArgsAndDependencies { + pub fn args_to_jobs_simple( + self, + params: &JobParams, + global_params: &GlobalParams, + f: F, + ) -> eyre::Result> + where + F: FnOnce(K, K::Args, &mut JobAndDependencies) -> eyre::Result, + { + let Self { + args: JobKindAndArgs { kind, args }, + dependencies, + } = self; + let mut dependencies = dependencies.args_to_jobs(params, global_params)?; + let job = f(kind, args, &mut dependencies)?; + Ok(JobAndDependencies { + job: JobAndKind { kind, job }, + dependencies, + }) + } +} + +impl>, D: JobKind> + JobArgsAndDependencies> +{ + pub fn args_to_jobs_external_simple( + self, + params: &JobParams, + global_params: &GlobalParams, + f: F, + ) -> eyre::Result<( + C::AdditionalJobData, + ::JobsAndKinds, + )> + where + F: FnOnce( + external::ExternalCommandArgs, + &mut JobAndDependencies, + ) -> eyre::Result, + { + let Self { + args: JobKindAndArgs { kind: _, args }, + dependencies, + } = self; + let mut dependencies = dependencies.args_to_jobs(params, global_params)?; + let additional_job_data = f(args, &mut dependencies)?; + Ok((additional_job_data, dependencies)) + } +} + +pub trait JobDependencies: 'static + Send + Sync + Hash + Eq + fmt::Debug + Copy { + type KindsAndArgs: 'static + Send + Sync + Hash + Eq + fmt::Debug + Clone; + type JobsAndKinds: 'static + Send + Sync + Hash + Eq + fmt::Debug; + fn kinds_dyn_extend>(self, dyn_kinds: &mut E); + fn kinds_dyn(self) -> Vec { + let mut retval = Vec::new(); + self.kinds_dyn_extend(&mut retval); + retval + } + fn into_dyn_jobs_extend>(jobs: Self::JobsAndKinds, dyn_jobs: &mut E); + fn into_dyn_jobs(jobs: Self::JobsAndKinds) -> Vec { + let mut retval = Vec::new(); + Self::into_dyn_jobs_extend(jobs, &mut retval); + retval + } + #[track_caller] + fn from_dyn_args_prefix>( + args: &mut I, + ) -> Self::KindsAndArgs; + #[track_caller] + fn from_dyn_args>(args: I) -> Self::KindsAndArgs { + let mut iter = args.into_iter(); + let retval = Self::from_dyn_args_prefix(&mut iter); + if iter.next().is_some() { + panic!("wrong number of dependencies"); + } + retval + } +} + +pub trait JobDependenciesHasBase: JobDependencies { + fn base_job_args(args: &Self::KindsAndArgs) -> &BaseJobArgs; + fn base_job(jobs: &Self::JobsAndKinds) -> &BaseJob; + #[track_caller] + fn base_job_args_dyn(dependencies_args: &[DynJobArgs]) -> &BaseJobArgs; + #[track_caller] + fn base_job_dyn(dependencies: &[DynJob]) -> &BaseJob; +} + +impl JobDependencies for JobKindAndDependencies { + type KindsAndArgs = JobArgsAndDependencies; + type JobsAndKinds = JobAndDependencies; + + fn kinds_dyn_extend>(self, dyn_kinds: &mut E) { + let Self { kind, dependencies } = self; + dependencies.kinds_dyn_extend(dyn_kinds); + dyn_kinds.extend([DynJobKind::new(kind)]); + } + + fn into_dyn_jobs_extend>( + jobs: Self::JobsAndKinds, + dyn_jobs: &mut E, + ) { + let JobAndDependencies { job, dependencies } = jobs; + K::Dependencies::into_dyn_jobs_extend(dependencies, dyn_jobs); + dyn_jobs.extend([job.into()]); + } + + #[track_caller] + fn from_dyn_args_prefix>( + args: &mut I, + ) -> Self::KindsAndArgs { + let dependencies = K::Dependencies::from_dyn_args_prefix(args); + let Some(args) = args.next() else { + panic!("wrong number of dependencies"); + }; + match args.downcast() { + Ok(args) => JobArgsAndDependencies { args, dependencies }, + Err(args) => { + panic!( + "wrong type of dependency, expected {} got:\n{args:?}", + std::any::type_name::() + ) + } + } + } +} + +impl JobDependenciesHasBase for JobKindAndDependencies { + fn base_job_args(args: &Self::KindsAndArgs) -> &BaseJobArgs { + args.base_job_args() + } + + fn base_job(jobs: &Self::JobsAndKinds) -> &BaseJob { + jobs.base_job() + } + + #[track_caller] + fn base_job_args_dyn(dependencies_args: &[DynJobArgs]) -> &BaseJobArgs { + let [dependencies_args @ .., args] = dependencies_args else { + panic!("wrong number of dependencies"); + }; + let Some((kind, args)) = args.downcast_ref::() else { + panic!( + "wrong type of dependency, expected {} got:\n{args:?}", + std::any::type_name::() + ) + }; + kind.base_job_args_dyn(args, dependencies_args) + } + + #[track_caller] + fn base_job_dyn(dependencies: &[DynJob]) -> &BaseJob { + let [dependencies @ .., job] = dependencies else { + panic!("wrong number of dependencies"); + }; + let Some((kind, job)) = job.downcast_ref::() else { + panic!( + "wrong type of dependency, expected {} got:\n{job:?}", + std::any::type_name::() + ) + }; + kind.base_job_dyn(job, dependencies) + } +} + +macro_rules! impl_job_dependencies { + (@impl $(($v:ident: $T:ident),)*) => { + impl<$($T: JobDependencies),*> JobDependencies for ($($T,)*) { + type KindsAndArgs = ($($T::KindsAndArgs,)*); + type JobsAndKinds = ($($T::JobsAndKinds,)*); + + fn kinds_dyn_extend>(self, dyn_kinds: &mut E) { + #![allow(unused_variables)] + let ($($v,)*) = self; + $($T::kinds_dyn_extend($v, dyn_kinds);)* + } + + fn into_dyn_jobs_extend>( + jobs: Self::JobsAndKinds, + dyn_jobs: &mut E, + ) { + #![allow(unused_variables)] + let ($($v,)*) = jobs; + $($T::into_dyn_jobs_extend($v, dyn_jobs);)* + } + + #[track_caller] + fn from_dyn_args_prefix>( + args: &mut I, + ) -> Self::KindsAndArgs { + #![allow(unused_variables)] + $(let $v = $T::from_dyn_args_prefix(args);)* + ($($v,)*) + } + } + }; + ($($first:tt, $($rest:tt,)*)?) => { + impl_job_dependencies!(@impl $($first, $($rest,)*)?); + $(impl_job_dependencies!($($rest,)*);)? + }; +} + +impl_job_dependencies! { + (v0: T0), + (v1: T1), + (v2: T2), + (v3: T3), + (v4: T4), + (v5: T5), + (v6: T6), + (v7: T7), + (v8: T8), + (v9: T9), + (v10: T10), + (v11: T11), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct JobParams { + main_module: Module, +} + +impl AsRef for JobParams { + fn as_ref(&self) -> &Self { + self + } +} + +impl JobParams { + pub fn new_canonical(main_module: Module) -> Self { + Self { main_module } + } + pub fn new(main_module: impl AsRef>) -> Self { + Self::new_canonical(main_module.as_ref().canonical()) + } + pub fn main_module(&self) -> &Module { + &self.main_module + } +} + +#[derive(Clone, Debug)] +pub struct GlobalParams { + top_level_cmd: Option, + application_name: Interned, +} + +impl AsRef for GlobalParams { + fn as_ref(&self) -> &Self { + self + } +} + +impl GlobalParams { + pub fn new(top_level_cmd: Option, application_name: impl AsRef) -> Self { + Self { + top_level_cmd, + application_name: application_name.as_ref().intern(), + } + } + pub fn top_level_cmd(&self) -> Option<&clap::Command> { + self.top_level_cmd.as_ref() + } + pub fn into_top_level_cmd(self) -> Option { + self.top_level_cmd + } + pub fn extract_clap_error(&self, e: eyre::Report) -> eyre::Result { + let e = e.downcast::()?; + Ok(match &self.top_level_cmd { + Some(cmd) => e.with_cmd(cmd), + None => e, + }) + } + pub fn exit_if_clap_error(&self, e: eyre::Report) -> eyre::Report { + match self.extract_clap_error(e) { + Ok(e) => e.exit(), + Err(e) => e, + } + } + pub fn clap_error( + &self, + kind: clap::error::ErrorKind, + message: impl fmt::Display, + ) -> clap::Error { + match self.top_level_cmd.clone() { + Some(top_level_cmd) => top_level_cmd.clone().error(kind, message), + None => clap::Error::raw(kind, message), + } + } + pub fn application_name(&self) -> Interned { + self.application_name + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct CommandParams { + pub command_line: Interned<[Interned]>, + pub current_dir: Option>, +} + +impl CommandParams { + fn to_unix_shell_line( + self, + output: &mut String, + mut escape_arg: impl FnMut(&OsStr, &mut String) -> Result<(), E>, + ) -> Result<(), E> { + let Self { + command_line, + current_dir, + } = self; + let mut end = None; + let mut separator = if let Some(current_dir) = current_dir { + output.push_str("(cd "); + end = Some(")"); + if !current_dir + .as_os_str() + .as_encoded_bytes() + .first() + .is_some_and(|ch| ch.is_ascii_alphanumeric() || matches!(ch, b'/' | b'\\' | b'.')) + { + output.push_str("-- "); + } + escape_arg(current_dir.as_ref(), output)?; + "; exec -- " + } else { + "" + }; + for arg in command_line { + output.push_str(separator); + separator = " "; + escape_arg(&arg, output)?; + } + if let Some(end) = end { + output.push_str(end); + } + Ok(()) + } +} + +pub trait JobKindHelper: 'static + Send + Sync + Hash + Eq + fmt::Debug + Copy { + fn base_job_args<'a>( + self, + args: &'a ::Args, + dependencies: &'a <::Dependencies as JobDependencies>::KindsAndArgs, + ) -> &'a BaseJobArgs + where + Self: JobKind; + fn base_job<'a>( + self, + job: &'a ::Job, + dependencies: &'a <::Dependencies as JobDependencies>::JobsAndKinds, + ) -> &'a BaseJob + where + Self: JobKind; + #[track_caller] + fn base_job_args_dyn<'a>( + self, + args: &'a ::Args, + dependencies_args: &'a [DynJobArgs], + ) -> &'a BaseJobArgs + where + Self: JobKind; + #[track_caller] + fn base_job_dyn<'a>( + self, + job: &'a ::Job, + dependencies: &'a [DynJob], + ) -> &'a BaseJob + where + Self: JobKind; +} + +impl> JobKindHelper for K { + fn base_job_args<'a>( + self, + _args: &'a ::Args, + dependencies: &'a <::Dependencies as JobDependencies>::KindsAndArgs, + ) -> &'a BaseJobArgs { + K::Dependencies::base_job_args(dependencies) + } + fn base_job<'a>( + self, + _job: &'a ::Job, + dependencies: &'a <::Dependencies as JobDependencies>::JobsAndKinds, + ) -> &'a BaseJob { + K::Dependencies::base_job(dependencies) + } + #[track_caller] + fn base_job_args_dyn<'a>( + self, + _args: &'a ::Args, + dependencies_args: &'a [DynJobArgs], + ) -> &'a BaseJobArgs { + K::Dependencies::base_job_args_dyn(dependencies_args) + } + #[track_caller] + fn base_job_dyn<'a>( + self, + _job: &'a ::Job, + dependencies: &'a [DynJob], + ) -> &'a BaseJob { + K::Dependencies::base_job_dyn(dependencies) + } +} + +pub trait JobKind: JobKindHelper { + type Args: ToArgs; + type Job: 'static + Send + Sync + Hash + Eq + fmt::Debug + Serialize + DeserializeOwned; + type Dependencies: JobDependencies; + fn dependencies(self) -> Self::Dependencies; + fn args_to_jobs( + args: JobArgsAndDependencies, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result>; + fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]>; + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]>; + fn name(self) -> Interned; + fn external_command_params(self, job: &Self::Job) -> Option; + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + params: &JobParams, + global_params: &GlobalParams, + acquired_job: &mut AcquiredJob, + ) -> eyre::Result>; + fn subcommand_hidden(self) -> bool { + false + } + fn external_program(self) -> Option> { + None + } +} + +trait DynJobKindTrait: 'static + Send + Sync + fmt::Debug { + fn as_any(&self) -> &dyn Any; + fn as_arc_any(self: Arc) -> Arc; + fn eq_dyn(&self, other: &dyn DynJobKindTrait) -> bool; + fn hash_dyn(&self, state: &mut dyn Hasher); + fn dependencies_kinds_dyn(&self) -> Vec; + fn args_group_id_dyn(&self) -> Option; + fn augment_args_dyn(&self, cmd: clap::Command) -> clap::Command; + fn augment_args_for_update_dyn(&self, cmd: clap::Command) -> clap::Command; + fn from_arg_matches_dyn( + &self, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result; + fn name_dyn(&self) -> Interned; + fn subcommand_hidden_dyn(&self) -> bool; + fn deserialize_job_from_json_str(self: Arc, json: &str) -> serde_json::Result; + fn deserialize_job_from_json_value( + self: Arc, + json: &serde_json::Value, + ) -> serde_json::Result; +} + +impl DynJobKindTrait for K { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_arc_any(self: Arc) -> Arc { + self + } + + fn eq_dyn(&self, other: &dyn DynJobKindTrait) -> bool { + other + .as_any() + .downcast_ref::() + .is_some_and(|other| self == other) + } + + fn hash_dyn(&self, mut state: &mut dyn Hasher) { + self.hash(&mut state); + } + + fn dependencies_kinds_dyn(&self) -> Vec { + self.dependencies().kinds_dyn() + } + + fn args_group_id_dyn(&self) -> Option { + ::group_id() + } + + fn augment_args_dyn(&self, cmd: clap::Command) -> clap::Command { + ::augment_args(cmd) + } + + fn augment_args_for_update_dyn(&self, cmd: clap::Command) -> clap::Command { + ::augment_args_for_update(cmd) + } + + fn from_arg_matches_dyn( + &self, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result { + Ok(DynJobArgs::new( + *self, + ::from_arg_matches_mut(matches)?, + )) + } + + fn name_dyn(&self) -> Interned { + self.name() + } + + fn subcommand_hidden_dyn(&self) -> bool { + self.subcommand_hidden() + } + + fn deserialize_job_from_json_str(self: Arc, json: &str) -> serde_json::Result { + Ok(DynJob::from_arc(self, serde_json::from_str(json)?)) + } + + fn deserialize_job_from_json_value( + self: Arc, + json: &serde_json::Value, + ) -> serde_json::Result { + Ok(DynJob::from_arc(self, Deserialize::deserialize(json)?)) + } +} + +#[derive(Clone)] +pub struct DynJobKind(Arc); + +impl DynJobKind { + pub fn from_arc(job_kind: Arc) -> Self { + Self(job_kind) + } + pub fn new(job_kind: K) -> Self { + Self(Arc::new(job_kind)) + } + pub fn type_id(&self) -> TypeId { + DynJobKindTrait::as_any(&*self.0).type_id() + } + pub fn downcast(&self) -> Option { + DynJobKindTrait::as_any(&*self.0).downcast_ref().copied() + } + pub fn downcast_arc(self) -> Result, Self> { + if self.downcast::().is_some() { + Ok(Arc::downcast::(self.0.as_arc_any()) + .ok() + .expect("already checked type")) + } else { + Err(self) + } + } + pub fn dependencies_kinds(&self) -> Vec { + DynJobKindTrait::dependencies_kinds_dyn(&*self.0) + } + pub fn args_group_id(&self) -> Option { + DynJobKindTrait::args_group_id_dyn(&*self.0) + } + pub fn augment_args(&self, cmd: clap::Command) -> clap::Command { + DynJobKindTrait::augment_args_dyn(&*self.0, cmd) + } + pub fn augment_args_for_update(&self, cmd: clap::Command) -> clap::Command { + DynJobKindTrait::augment_args_for_update_dyn(&*self.0, cmd) + } + pub fn from_arg_matches( + &self, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result { + DynJobKindTrait::from_arg_matches_dyn(&*self.0, matches) + } + pub fn name(&self) -> Interned { + DynJobKindTrait::name_dyn(&*self.0) + } + pub fn subcommand_hidden(&self) -> bool { + DynJobKindTrait::subcommand_hidden_dyn(&*self.0) + } + pub fn deserialize_job_from_json_str(self, json: &str) -> serde_json::Result { + DynJobKindTrait::deserialize_job_from_json_str(self.0, json) + } + pub fn deserialize_job_from_json_value( + self, + json: &serde_json::Value, + ) -> serde_json::Result { + DynJobKindTrait::deserialize_job_from_json_value(self.0, json) + } + fn make_subcommand_without_args(&self) -> clap::Command { + clap::Command::new(Interned::into_inner(self.name())).hide(self.subcommand_hidden()) + } + pub fn make_subcommand(&self) -> clap::Command { + let mut subcommand = self.make_subcommand_without_args(); + for dependency in self.dependencies_kinds() { + subcommand = dependency.augment_args(subcommand); + } + self.augment_args(subcommand) + } + pub fn make_subcommand_for_update(&self) -> clap::Command { + let mut subcommand = self.make_subcommand_without_args(); + for dependency in self.dependencies_kinds() { + subcommand = dependency.augment_args_for_update(subcommand); + } + self.augment_args_for_update(subcommand) + } +} + +impl Hash for DynJobKind { + fn hash(&self, state: &mut H) { + self.type_id().hash(state); + DynJobKindTrait::hash_dyn(&*self.0, state); + } +} + +impl PartialEq for DynJobKind { + fn eq(&self, other: &Self) -> bool { + DynJobKindTrait::eq_dyn(&*self.0, &*other.0) + } +} + +impl Eq for DynJobKind {} + +impl fmt::Debug for DynJobKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Serialize for DynJobKind { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.name().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynJobKind { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let name = Cow::::deserialize(deserializer)?; + match Self::registry().get_by_name(&name) { + Some(retval) => Ok(retval.clone()), + None => Err(D::Error::custom(format_args!( + "unknown job kind: name not found in registry: {name:?}" + ))), + } + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct DynJobKindValueParser; + +#[derive(Clone, PartialEq, Eq, Hash)] +struct DynJobKindValueEnum { + name: Interned, + job_kind: DynJobKind, +} + +impl clap::ValueEnum for DynJobKindValueEnum { + fn value_variants<'a>() -> &'a [Self] { + Interned::into_inner( + registry::JobKindRegistrySnapshot::get() + .iter_with_names() + .map(|(name, job_kind)| Self { + name, + job_kind: job_kind.clone(), + }) + .collect(), + ) + } + + fn to_possible_value(&self) -> Option { + Some(clap::builder::PossibleValue::new(Interned::into_inner( + self.name, + ))) + } +} + +impl clap::builder::TypedValueParser for DynJobKindValueParser { + type Value = DynJobKind; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> clap::error::Result { + clap::builder::EnumValueParser::::new() + .parse_ref(cmd, arg, value) + .map(|v| v.job_kind) + } + + fn possible_values( + &self, + ) -> Option + '_>> { + static ENUM_VALUE_PARSER: OnceLock> = + OnceLock::new(); + ENUM_VALUE_PARSER + .get_or_init(clap::builder::EnumValueParser::::new) + .possible_values() + } +} + +impl clap::builder::ValueParserFactory for DynJobKind { + type Parser = DynJobKindValueParser; + + fn value_parser() -> Self::Parser { + DynJobKindValueParser::default() + } +} + +trait DynExtendInternedStr { + fn extend_from_slice(&mut self, items: &[Interned]); +} + +impl Extend> for dyn DynExtendInternedStr + '_ { + fn extend>>(&mut self, iter: T) { + let mut buf = [Interned::default(); 64]; + let mut buf_len = 0; + iter.into_iter().for_each(|item| { + buf[buf_len] = item; + buf_len += 1; + if buf_len == buf.len() { + ::extend_from_slice(self, &buf); + buf_len = 0; + } + }); + if buf_len > 0 { + ::extend_from_slice( + self, + &buf[..buf_len], + ); + } + } +} + +impl>> DynExtendInternedStr for T { + fn extend_from_slice(&mut self, items: &[Interned]) { + self.extend(items.iter().copied()); + } +} + +#[derive(PartialEq, Eq, Hash, Clone)] +struct DynJobArgsInner(JobKindAndArgs); + +impl fmt::Debug for DynJobArgsInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(JobKindAndArgs { kind, args }) = self; + f.debug_struct("DynJobArgs") + .field("kind", kind) + .field("args", args) + .finish() + } +} + +trait DynJobArgsTrait: 'static + Send + Sync + fmt::Debug { + fn as_any(&self) -> &dyn Any; + fn as_arc_any(self: Arc) -> Arc; + fn kind_type_id(&self) -> TypeId; + fn eq_dyn(&self, other: &dyn DynJobArgsTrait) -> bool; + fn hash_dyn(&self, state: &mut dyn Hasher); + fn kind(&self) -> DynJobKind; + fn to_args_extend_vec(&self, args: Vec>) -> Vec>; + fn clone_into_arc(&self) -> Arc; + fn update_from_arg_matches( + &mut self, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result<()>; + #[track_caller] + fn args_to_jobs( + self: Arc, + dependencies_args: Vec, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<(DynJob, Vec)>; + #[track_caller] + fn base_job_args_dyn<'a>(&'a self, dependencies_args: &'a [DynJobArgs]) -> &'a BaseJobArgs; +} + +impl DynJobArgsTrait for DynJobArgsInner { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_arc_any(self: Arc) -> Arc { + self + } + + fn kind_type_id(&self) -> TypeId { + TypeId::of::() + } + + fn eq_dyn(&self, other: &dyn DynJobArgsTrait) -> bool { + other + .as_any() + .downcast_ref::() + .is_some_and(|other| self == other) + } + + fn hash_dyn(&self, mut state: &mut dyn Hasher) { + self.hash(&mut state); + } + + fn kind(&self) -> DynJobKind { + DynJobKind::new(self.0.kind) + } + + fn to_args_extend_vec(&self, args: Vec>) -> Vec> { + let mut writer = ArgsWriter(args); + self.0.args.to_args(&mut writer); + writer.0 + } + + fn clone_into_arc(&self) -> Arc { + Arc::new(self.clone()) + } + + fn update_from_arg_matches( + &mut self, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result<()> { + clap::FromArgMatches::update_from_arg_matches_mut(&mut self.0.args, matches) + } + + #[track_caller] + fn args_to_jobs( + self: Arc, + dependencies_args: Vec, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<(DynJob, Vec)> { + let JobAndDependencies { job, dependencies } = JobArgsAndDependencies { + args: Arc::unwrap_or_clone(self).0, + dependencies: K::Dependencies::from_dyn_args(dependencies_args), + } + .args_to_jobs(params, global_params)?; + Ok((job.into(), K::Dependencies::into_dyn_jobs(dependencies))) + } + + #[track_caller] + fn base_job_args_dyn<'a>(&'a self, dependencies_args: &'a [DynJobArgs]) -> &'a BaseJobArgs { + self.0 + .kind + .base_job_args_dyn(&self.0.args, dependencies_args) + } +} + +#[derive(Clone)] +pub struct DynJobArgs(Arc); + +impl DynJobArgs { + pub fn new(kind: K, args: K::Args) -> Self { + Self(Arc::new(DynJobArgsInner(JobKindAndArgs { kind, args }))) + } + pub fn kind_type_id(&self) -> TypeId { + DynJobArgsTrait::kind_type_id(&*self.0) + } + pub fn downcast_ref(&self) -> Option<(&K, &K::Args)> { + let DynJobArgsInner::(JobKindAndArgs { kind, args }) = + DynJobArgsTrait::as_any(&*self.0).downcast_ref()?; + Some((kind, args)) + } + pub fn downcast(self) -> Result, Self> { + if self.downcast_ref::().is_some() { + let this = Arc::downcast::>(self.0.as_arc_any()) + .ok() + .expect("already checked type"); + Ok(Arc::unwrap_or_clone(this).0) + } else { + Err(self) + } + } + pub fn kind(&self) -> DynJobKind { + DynJobArgsTrait::kind(&*self.0) + } + pub fn to_args_vec(&self) -> Vec> { + self.to_args_extend_vec(Vec::new()) + } + pub fn to_args_extend_vec(&self, args: Vec>) -> Vec> { + DynJobArgsTrait::to_args_extend_vec(&*self.0, args) + } + fn make_mut(&mut self) -> &mut dyn DynJobArgsTrait { + // can't just return the reference if the first get_mut returns Some since + // as of rustc 1.90.0 this causes a false-positive lifetime error. + if Arc::get_mut(&mut self.0).is_none() { + self.0 = DynJobArgsTrait::clone_into_arc(&*self.0); + } + Arc::get_mut(&mut self.0).expect("clone_into_arc returns a new arc with a ref-count of 1") + } + pub fn update_from_arg_matches( + &mut self, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result<()> { + DynJobArgsTrait::update_from_arg_matches(self.make_mut(), matches) + } + pub fn args_to_jobs( + self, + dependencies_args: Vec, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<(DynJob, Vec)> { + DynJobArgsTrait::args_to_jobs(self.0, dependencies_args, params, global_params) + } + #[track_caller] + pub fn base_job_args_dyn<'a>(&'a self, dependencies_args: &'a [DynJobArgs]) -> &'a BaseJobArgs { + DynJobArgsTrait::base_job_args_dyn(&*self.0, dependencies_args) + } +} + +impl Hash for DynJobArgs { + fn hash(&self, state: &mut H) { + self.kind_type_id().hash(state); + DynJobArgsTrait::hash_dyn(&*self.0, state); + } +} + +impl PartialEq for DynJobArgs { + fn eq(&self, other: &Self) -> bool { + DynJobArgsTrait::eq_dyn(&*self.0, &*other.0) + } +} + +impl Eq for DynJobArgs {} + +impl fmt::Debug for DynJobArgs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[derive(PartialEq, Eq, Hash)] +struct DynJobInner { + kind: Arc, + job: K::Job, + inputs: Interned<[JobItemName]>, + outputs: Interned<[JobItemName]>, + external_command_params: Option, +} + +impl> Clone for DynJobInner { + fn clone(&self) -> Self { + Self { + kind: self.kind.clone(), + job: self.job.clone(), + inputs: self.inputs, + outputs: self.outputs, + external_command_params: self.external_command_params, + } + } +} + +impl fmt::Debug for DynJobInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + kind, + job, + inputs, + outputs, + external_command_params, + } = self; + f.debug_struct("DynJob") + .field("kind", kind) + .field("job", job) + .field("inputs", inputs) + .field("outputs", outputs) + .field("external_command_params", external_command_params) + .finish() + } +} + +trait DynJobTrait: 'static + Send + Sync + fmt::Debug { + fn as_any(&self) -> &dyn Any; + fn as_arc_any(self: Arc) -> Arc; + fn eq_dyn(&self, other: &dyn DynJobTrait) -> bool; + fn hash_dyn(&self, state: &mut dyn Hasher); + fn kind_type_id(&self) -> TypeId; + fn kind(&self) -> DynJobKind; + fn inputs(&self) -> Interned<[JobItemName]>; + fn outputs(&self) -> Interned<[JobItemName]>; + fn external_command_params(&self) -> Option; + fn serialize_to_json_ascii(&self) -> serde_json::Result; + fn serialize_to_json_value(&self) -> serde_json::Result; + fn run( + &self, + inputs: &[JobItem], + params: &JobParams, + global_params: &GlobalParams, + acquired_job: &mut AcquiredJob, + ) -> eyre::Result>; + #[track_caller] + fn base_job_dyn<'a>(&'a self, dependencies: &'a [DynJob]) -> &'a BaseJob; +} + +impl DynJobTrait for DynJobInner { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_arc_any(self: Arc) -> Arc { + self + } + + fn eq_dyn(&self, other: &dyn DynJobTrait) -> bool { + other + .as_any() + .downcast_ref::() + .is_some_and(|other| self == other) + } + + fn hash_dyn(&self, mut state: &mut dyn Hasher) { + self.hash(&mut state); + } + + fn kind_type_id(&self) -> TypeId { + TypeId::of::() + } + + fn kind(&self) -> DynJobKind { + DynJobKind(self.kind.clone()) + } + + fn inputs(&self) -> Interned<[JobItemName]> { + self.inputs + } + + fn outputs(&self) -> Interned<[JobItemName]> { + self.outputs + } + + fn external_command_params(&self) -> Option { + self.external_command_params + } + + fn serialize_to_json_ascii(&self) -> serde_json::Result { + crate::util::serialize_to_json_ascii(&self.job) + } + + fn serialize_to_json_value(&self) -> serde_json::Result { + serde_json::to_value(&self.job) + } + + fn run( + &self, + inputs: &[JobItem], + params: &JobParams, + global_params: &GlobalParams, + acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + self.kind + .run(&self.job, inputs, params, global_params, acquired_job) + } + + #[track_caller] + fn base_job_dyn<'a>(&'a self, dependencies: &'a [DynJob]) -> &'a BaseJob { + self.kind.base_job_dyn(&self.job, dependencies) + } +} + +#[derive(Clone, Debug)] +pub struct DynJob(Arc); + +impl DynJob { + pub fn from_arc(job_kind: Arc, job: K::Job) -> Self { + let inputs = job_kind.inputs(&job); + let outputs = job_kind.outputs(&job); + let external_command_params = job_kind.external_command_params(&job); + Self(Arc::new(DynJobInner { + kind: job_kind, + job, + inputs, + outputs, + external_command_params, + })) + } + pub fn new(job_kind: K, job: K::Job) -> Self { + Self::from_arc(Arc::new(job_kind), job) + } + pub fn kind_type_id(&self) -> TypeId { + self.0.kind_type_id() + } + pub fn downcast_ref(&self) -> Option<(&K, &K::Job)> { + let DynJobInner { kind, job, .. } = self.0.as_any().downcast_ref()?; + Some((kind, job)) + } + pub fn downcast>(self) -> Result, Self> { + if self.kind_type_id() == TypeId::of::() { + let DynJobInner { kind, job, .. } = Arc::unwrap_or_clone( + self.0 + .as_arc_any() + .downcast::>() + .expect("already checked type"), + ); + Ok(JobAndKind { kind: *kind, job }) + } else { + Err(self) + } + } + pub fn kind(&self) -> DynJobKind { + DynJobTrait::kind(&*self.0) + } + pub fn inputs(&self) -> Interned<[JobItemName]> { + DynJobTrait::inputs(&*self.0) + } + pub fn outputs(&self) -> Interned<[JobItemName]> { + DynJobTrait::outputs(&*self.0) + } + pub fn serialize_to_json_ascii(&self) -> serde_json::Result { + DynJobTrait::serialize_to_json_ascii(&*self.0) + } + pub fn serialize_to_json_value(&self) -> serde_json::Result { + DynJobTrait::serialize_to_json_value(&*self.0) + } + pub fn external_command_params(&self) -> Option { + DynJobTrait::external_command_params(&*self.0) + } + #[track_caller] + pub fn internal_command_params_with_program_prefix( + &self, + internal_program_prefix: &[Interned], + platform: Option<&DynPlatform>, + extra_args: &[Interned], + ) -> CommandParams { + let mut command_line = internal_program_prefix.to_vec(); + let command_line = match RunSingleJob::try_add_subcommand(platform, self, &mut command_line) + { + Ok(()) => { + command_line.extend_from_slice(extra_args); + Intern::intern_owned(command_line) + } + Err(e) => panic!("Serializing job {:?} failed: {e}", self.kind().name()), + }; + CommandParams { + command_line, + current_dir: None, + } + } + #[track_caller] + pub fn internal_command_params( + &self, + platform: Option<&DynPlatform>, + extra_args: &[Interned], + ) -> CommandParams { + self.internal_command_params_with_program_prefix( + &[program_name_for_internal_jobs()], + platform, + extra_args, + ) + } + #[track_caller] + pub fn command_params_with_internal_program_prefix( + &self, + internal_program_prefix: &[Interned], + platform: Option<&DynPlatform>, + extra_args: &[Interned], + ) -> CommandParams { + match self.external_command_params() { + Some(v) => v, + None => self.internal_command_params_with_program_prefix( + internal_program_prefix, + platform, + extra_args, + ), + } + } + #[track_caller] + pub fn command_params( + &self, + platform: Option<&DynPlatform>, + extra_args: &[Interned], + ) -> CommandParams { + self.command_params_with_internal_program_prefix( + &[program_name_for_internal_jobs()], + platform, + extra_args, + ) + } + pub fn run( + &self, + inputs: &[JobItem], + params: &JobParams, + global_params: &GlobalParams, + acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + DynJobTrait::run(&*self.0, inputs, params, global_params, acquired_job) + } + #[track_caller] + pub fn base_job_dyn<'a>(&'a self, dependencies: &'a [DynJob]) -> &'a BaseJob { + DynJobTrait::base_job_dyn(&*self.0, dependencies) + } +} + +impl Eq for DynJob {} + +impl PartialEq for DynJob { + fn eq(&self, other: &Self) -> bool { + DynJobTrait::eq_dyn(&*self.0, &*other.0) + } +} + +impl Hash for DynJob { + fn hash(&self, state: &mut H) { + DynJobTrait::hash_dyn(&*self.0, state); + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename = "DynJob")] +struct DynJobSerde { + kind: DynJobKind, + job: serde_json::Value, +} + +impl Serialize for DynJob { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + DynJobSerde { + kind: self.kind(), + job: self.serialize_to_json_value().map_err(S::Error::custom)?, + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynJob { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let DynJobSerde { kind, job } = Deserialize::deserialize(deserializer)?; + kind.deserialize_job_from_json_value(&job) + .map_err(D::Error::custom) + } +} + +pub trait RunBuild: Sized { + fn main_without_platform(application_name: impl AsRef, make_params: F) + where + Self: clap::Parser + Clone, + F: FnOnce(Self, Extra) -> eyre::Result, + { + let application_name = application_name.as_ref(); + match Self::try_main_without_platform(application_name, make_params) { + Ok(()) => {} + Err(e) => { + let e = GlobalParams::new(Some(Self::command()), application_name) + .exit_if_clap_error(e); + eprintln!("{e:#}"); + std::process::exit(1); + } + } + } + fn try_main_without_platform( + application_name: impl AsRef, + make_params: F, + ) -> eyre::Result<()> + where + Self: clap::Parser + Clone, + F: FnOnce(Self, Extra) -> eyre::Result, + { + let args = Self::parse(); + let global_params = GlobalParams::new(Some(Self::command()), application_name); + args.clone() + .run_without_platform(|extra| make_params(args, extra), &global_params) + .map_err(|e| global_params.exit_if_clap_error(e)) + } + fn run_without_platform( + self, + make_params: F, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(Extra) -> eyre::Result; + fn get_platform(&self) -> Option<&DynPlatform>; + fn main(application_name: impl AsRef, make_params: F) + where + Self: clap::Parser + Clone, + F: FnOnce(Self, DynPlatform, Extra) -> eyre::Result, + { + let application_name = application_name.as_ref(); + match Self::try_main(application_name, make_params) { + Ok(()) => {} + Err(e) => { + let e = GlobalParams::new(Some(Self::command()), application_name) + .exit_if_clap_error(e); + eprintln!("{e:#}"); + std::process::exit(1); + } + } + } + fn try_main(application_name: impl AsRef, make_params: F) -> eyre::Result<()> + where + Self: clap::Parser + Clone, + F: FnOnce(Self, DynPlatform, Extra) -> eyre::Result, + { + let args = Self::parse(); + let global_params = GlobalParams::new(Some(Self::command()), application_name); + let Some(platform) = args.get_platform().cloned() else { + return args.handle_missing_platform(&global_params); + }; + args.clone() + .run( + |platform, extra| make_params(args, platform, extra), + platform, + &global_params, + ) + .map_err(|e| global_params.exit_if_clap_error(e)) + } + fn handle_missing_platform(self, global_params: &GlobalParams) -> eyre::Result<()> { + global_params + .clap_error( + clap::error::ErrorKind::MissingRequiredArgument, + "--platform is required", + ) + .exit(); + } + fn run( + self, + make_params: F, + platform: DynPlatform, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(DynPlatform, Extra) -> eyre::Result, + { + self.run_without_platform(|extra| make_params(platform, extra), global_params) + } +} + +impl RunBuild for JobArgsAndDependencies { + fn run_without_platform( + self, + make_params: F, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(NoArgs) -> eyre::Result, + { + let params = make_params(NoArgs)?; + self.args_to_jobs(¶ms, global_params)? + .run_without_platform(|_| Ok(params), global_params) + } + fn get_platform(&self) -> Option<&DynPlatform> { + self.base_job_args().platform.as_ref() + } + fn run( + self, + make_params: F, + platform: DynPlatform, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(DynPlatform, NoArgs) -> eyre::Result, + { + let params = make_params(platform.clone(), NoArgs)?; + self.args_to_jobs(¶ms, global_params)? + .run(|_, _| Ok(params), platform, global_params) + } +} + +impl RunBuild for JobAndDependencies { + fn run_without_platform( + self, + make_params: F, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(NoArgs) -> eyre::Result, + { + let params = make_params(NoArgs)?; + let Self { job, dependencies } = self; + let mut jobs = vec![DynJob::from(job)]; + K::Dependencies::into_dyn_jobs_extend(dependencies, &mut jobs); + let mut job_graph = JobGraph::new(); + job_graph.add_jobs(jobs); // add all at once to avoid recomputing graph properties multiple times + job_graph.run(¶ms, global_params) + } + fn get_platform(&self) -> Option<&DynPlatform> { + self.base_job().platform() + } + fn run( + self, + make_params: F, + platform: DynPlatform, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(DynPlatform, NoArgs) -> eyre::Result, + { + let params = make_params(platform, NoArgs)?; + let Self { job, dependencies } = self; + let mut jobs = vec![DynJob::from(job)]; + K::Dependencies::into_dyn_jobs_extend(dependencies, &mut jobs); + let mut job_graph = JobGraph::new(); + job_graph.add_jobs(jobs); // add all at once to avoid recomputing graph properties multiple times + job_graph.run(¶ms, global_params) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct RunSingleJob { + pub platform: Option, + pub job: DynJob, + pub extra: Extra, +} + +impl RunSingleJob { + pub const SUBCOMMAND_NAME: &'static str = "run-single-job"; + fn try_add_subcommand( + platform: Option<&DynPlatform>, + job: &DynJob, + subcommand_line: &mut Vec>, + ) -> serde_json::Result<()> { + let mut json = job.serialize_to_json_ascii()?; + json.insert_str(0, "--json="); + subcommand_line.push(Self::SUBCOMMAND_NAME.intern().into()); + if let Some(platform) = platform { + subcommand_line.push( + format!("--platform={}", platform.name()) + .intern_deref() + .into(), + ); + } + subcommand_line.push( + format!("--name={}", job.kind().name()) + .intern_deref() + .into(), + ); + subcommand_line.push(json.intern_deref().into()); + Ok(()) + } +} + +impl TryFrom> for RunSingleJob { + type Error = clap::Error; + + fn try_from(value: RunSingleJobClap) -> Result { + let RunSingleJobClap::RunSingleJob { + platform, + name: job_kind, + json, + extra, + } = value; + let name = job_kind.name(); + job_kind + .deserialize_job_from_json_str(&json) + .map_err(|e| { + clap::Error::raw( + clap::error::ErrorKind::ValueValidation, + format_args!("failed to parse job {name} from JSON: {e}"), + ) + }) + .map(|job| Self { + platform, + job, + extra, + }) + } +} + +#[derive(clap::Subcommand)] +enum RunSingleJobClap { + #[command(name = RunSingleJob::SUBCOMMAND_NAME, hide = true)] + RunSingleJob { + #[arg(long)] + platform: Option, + #[arg(long)] + name: DynJobKind, + #[arg(long)] + json: String, + #[command(flatten)] + extra: Extra, + }, +} + +impl clap::Subcommand for RunSingleJob { + fn augment_subcommands(cmd: clap::Command) -> clap::Command { + RunSingleJobClap::::augment_subcommands(cmd) + } + + fn augment_subcommands_for_update(cmd: clap::Command) -> clap::Command { + RunSingleJobClap::::augment_subcommands(cmd) + } + + fn has_subcommand(name: &str) -> bool { + RunSingleJobClap::::has_subcommand(name) + } +} + +impl clap::FromArgMatches for RunSingleJob { + fn from_arg_matches(matches: &clap::ArgMatches) -> clap::error::Result { + RunSingleJobClap::from_arg_matches(matches)?.try_into() + } + fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> clap::error::Result { + RunSingleJobClap::from_arg_matches_mut(matches)?.try_into() + } + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> clap::error::Result<()> { + *self = Self::from_arg_matches(matches)?; + Ok(()) + } + fn update_from_arg_matches_mut( + &mut self, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result<()> { + *self = Self::from_arg_matches_mut(matches)?; + Ok(()) + } +} + +impl RunBuild for RunSingleJob { + fn run_without_platform( + self, + make_params: F, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(Extra) -> eyre::Result, + { + let params = make_params(self.extra)?; + let mut job_graph = JobGraph::new(); + job_graph.add_jobs([self.job]); + job_graph.run(¶ms, global_params) + } + fn get_platform(&self) -> Option<&DynPlatform> { + self.platform.as_ref() + } +} + +#[derive(Clone, PartialEq, Eq, Hash, clap::Subcommand)] +pub enum Completions { + #[non_exhaustive] + Completions { + #[arg(default_value = Self::shell_str_from_env(), required = Self::shell_from_env().is_none())] + shell: clap_complete::aot::Shell, + }, +} + +impl Completions { + pub fn new(shell: clap_complete::aot::Shell) -> Self { + Self::Completions { shell } + } + pub fn from_env() -> Option { + Some(Self::Completions { + shell: Self::shell_from_env()?, + }) + } + fn shell_from_env() -> Option { + static SHELL: OnceLock> = OnceLock::new(); + *SHELL.get_or_init(clap_complete::aot::Shell::from_env) + } + fn shell_str_from_env() -> clap::builder::Resettable { + static SHELL_STR: OnceLock> = OnceLock::new(); + SHELL_STR + .get_or_init(|| Self::shell_from_env().map(|v| v.to_string())) + .as_deref() + .map(Into::into) + .into() + } +} + +impl RunBuild for Completions { + fn run_without_platform( + self, + _make_params: F, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(NoArgs) -> eyre::Result, + { + let Self::Completions { shell } = self; + let Some(cmd) = global_params.top_level_cmd() else { + eyre::bail!("completions command requires GlobalParams::top_level_cmd() to be Some"); + }; + let bin_name = cmd.get_bin_name().map(str::intern).unwrap_or_else(|| { + program_name_for_internal_jobs() + .to_interned_str() + .expect("program name is invalid UTF-8") + }); + clap_complete::aot::generate( + shell, + &mut cmd.clone(), + &*bin_name, + &mut std::io::BufWriter::new(std::io::stdout().lock()), + ); + Ok(()) + } + fn handle_missing_platform(self, global_params: &GlobalParams) -> eyre::Result<()> { + self.run_without_platform(|_| unreachable!(), global_params) + } + fn get_platform(&self) -> Option<&DynPlatform> { + None + } +} + +#[derive( + clap::Args, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Debug, + Default, + Serialize, + Deserialize, +)] +pub struct NoArgs; + +impl ToArgs for NoArgs { + fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { + let Self {} = self; + } +} + +#[derive(Clone, PartialEq, Eq, Hash, clap::Parser)] +pub enum BuildCli { + #[clap(flatten)] + Job(AnyJobSubcommand), + #[clap(flatten)] + RunSingleJob(RunSingleJob), + #[clap(flatten)] + Completions(Completions), + #[cfg(unix)] + #[clap(flatten)] + CreateUnixShellScript(CreateUnixShellScript), +} + +impl RunBuild for BuildCli { + fn run_without_platform( + self, + make_params: F, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(Extra) -> eyre::Result, + { + match self { + BuildCli::Job(v) => v.run_without_platform(make_params, global_params), + BuildCli::RunSingleJob(v) => v.run_without_platform(make_params, global_params), + BuildCli::Completions(v) => { + v.run_without_platform(|NoArgs {}| unreachable!(), global_params) + } + #[cfg(unix)] + BuildCli::CreateUnixShellScript(v) => { + v.run_without_platform(make_params, global_params) + } + } + } + fn handle_missing_platform(self, global_params: &GlobalParams) -> eyre::Result<()> { + match self { + BuildCli::Job(v) => v.handle_missing_platform(global_params), + BuildCli::RunSingleJob(v) => v.handle_missing_platform(global_params), + BuildCli::Completions(v) => v.handle_missing_platform(global_params), + #[cfg(unix)] + BuildCli::CreateUnixShellScript(v) => v.handle_missing_platform(global_params), + } + } + fn get_platform(&self) -> Option<&DynPlatform> { + match self { + BuildCli::Job(v) => v.get_platform(), + BuildCli::RunSingleJob(v) => v.get_platform(), + BuildCli::Completions(v) => v.get_platform(), + #[cfg(unix)] + BuildCli::CreateUnixShellScript(v) => v.get_platform(), + } + } + fn run( + self, + make_params: F, + platform: DynPlatform, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(DynPlatform, Extra) -> eyre::Result, + { + match self { + BuildCli::Job(v) => v.run(make_params, platform, global_params), + BuildCli::RunSingleJob(v) => v.run(make_params, platform, global_params), + BuildCli::Completions(v) => { + v.run(|_, NoArgs {}| unreachable!(), platform, global_params) + } + #[cfg(unix)] + BuildCli::CreateUnixShellScript(v) => v.run(make_params, platform, global_params), + } + } +} + +#[cfg(unix)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Subcommand)] +enum CreateUnixShellScriptInner { + CreateUnixShellScript { + #[arg(name = "i-know-this-is-incomplete", long, required = true, action = ArgAction::SetTrue)] + _incomplete: (), + #[command(subcommand)] + inner: AnyJobSubcommand, + }, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct CreateUnixShellScript(CreateUnixShellScriptInner); + +impl RunBuild for CreateUnixShellScript { + fn run_without_platform( + self, + make_params: F, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(Extra) -> eyre::Result, + { + let platform = self.get_platform().cloned(); + let CreateUnixShellScriptInner::CreateUnixShellScript { + _incomplete: (), + inner: + AnyJobSubcommand { + args, + dependencies_args, + extra, + }, + } = self.0; + let extra_args = extra.to_interned_args_vec(); + let params = make_params(extra)?; + let bin_name = global_params + .top_level_cmd() + .and_then(clap::Command::get_bin_name) + .map(|v| OsStr::new(v).intern()); + let (job, dependencies) = args.args_to_jobs(dependencies_args, ¶ms, global_params)?; + let mut job_graph = JobGraph::new(); + job_graph.add_jobs([job].into_iter().chain(dependencies)); + std::io::stdout().write_all( + job_graph + .to_unix_shell_script_with_internal_program_prefix( + &[bin_name.unwrap_or_else(|| program_name_for_internal_jobs())], + platform.as_ref(), + &extra_args, + ) + .as_bytes(), + )?; + Ok(()) + } + fn get_platform(&self) -> Option<&DynPlatform> { + let CreateUnixShellScriptInner::CreateUnixShellScript { inner, .. } = &self.0; + inner.get_platform() + } +} + +impl clap::FromArgMatches for CreateUnixShellScript { + fn from_arg_matches(matches: &clap::ArgMatches) -> Result { + clap::FromArgMatches::from_arg_matches(matches).map(Self) + } + fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> Result { + clap::FromArgMatches::from_arg_matches_mut(matches).map(Self) + } + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { + self.0.update_from_arg_matches(matches) + } + fn update_from_arg_matches_mut( + &mut self, + matches: &mut clap::ArgMatches, + ) -> Result<(), clap::Error> { + self.0.update_from_arg_matches_mut(matches) + } +} + +#[cfg(unix)] +impl clap::Subcommand for CreateUnixShellScript { + fn augment_subcommands(cmd: clap::Command) -> clap::Command { + CreateUnixShellScriptInner::::augment_subcommands(cmd) + } + + fn augment_subcommands_for_update(cmd: clap::Command) -> clap::Command { + CreateUnixShellScriptInner::::augment_subcommands_for_update(cmd) + } + + fn has_subcommand(name: &str) -> bool { + CreateUnixShellScriptInner::::has_subcommand(name) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct AnyJobSubcommand { + pub args: DynJobArgs, + pub dependencies_args: Vec, + pub extra: Extra, +} + +impl AnyJobSubcommand { + pub fn from_subcommand_arg_matches( + job_kind: &DynJobKind, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result { + let dependencies = job_kind.dependencies_kinds(); + let dependencies_args = Result::from_iter( + dependencies + .into_iter() + .map(|dependency| dependency.from_arg_matches(matches)), + )?; + Ok(Self { + args: job_kind.clone().from_arg_matches(matches)?, + dependencies_args, + extra: Extra::from_arg_matches_mut(matches)?, + }) + } + pub fn update_from_subcommand_arg_matches( + &mut self, + job_kind: &DynJobKind, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result<()> { + let Self { + args, + dependencies_args, + extra, + } = self; + if *job_kind == args.kind() { + for dependency in dependencies_args { + dependency.update_from_arg_matches(matches)?; + } + args.update_from_arg_matches(matches)?; + } else { + let dependencies = job_kind.dependencies_kinds(); + let new_dependencies_args = Result::from_iter( + dependencies + .into_iter() + .map(|dependency| dependency.from_arg_matches(matches)), + )?; + *args = job_kind.clone().from_arg_matches(matches)?; + *dependencies_args = new_dependencies_args; + } + extra.update_from_arg_matches_mut(matches) + } +} + +impl clap::Subcommand for AnyJobSubcommand { + fn augment_subcommands(mut cmd: clap::Command) -> clap::Command { + let snapshot = registry::JobKindRegistrySnapshot::get(); + for job_kind in &snapshot { + cmd = cmd.subcommand(Extra::augment_args(job_kind.make_subcommand())); + } + cmd + } + + fn augment_subcommands_for_update(mut cmd: clap::Command) -> clap::Command { + let snapshot = registry::JobKindRegistrySnapshot::get(); + for job_kind in &snapshot { + cmd = cmd.subcommand(Extra::augment_args_for_update( + job_kind.make_subcommand_for_update(), + )); + } + cmd + } + + fn has_subcommand(name: &str) -> bool { + registry::JobKindRegistrySnapshot::get() + .get_by_name(name) + .is_some() + } +} + +impl clap::FromArgMatches for AnyJobSubcommand { + fn from_arg_matches(matches: &clap::ArgMatches) -> clap::error::Result { + Self::from_arg_matches_mut(&mut matches.clone()) + } + + fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> clap::error::Result { + if let Some((name, mut matches)) = matches.remove_subcommand() { + let job_kind_registry_snapshot = registry::JobKindRegistrySnapshot::get(); + if let Some(job_kind) = job_kind_registry_snapshot.get_by_name(&name) { + Self::from_subcommand_arg_matches(job_kind, &mut matches) + } else { + Err(clap::Error::raw( + clap::error::ErrorKind::InvalidSubcommand, + format!("the subcommand '{name}' wasn't recognized"), + )) + } + } else { + Err(clap::Error::raw( + clap::error::ErrorKind::MissingSubcommand, + "a subcommand is required but one was not provided", + )) + } + } + + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> clap::error::Result<()> { + Self::update_from_arg_matches_mut(self, &mut matches.clone()) + } + + fn update_from_arg_matches_mut( + &mut self, + matches: &mut clap::ArgMatches, + ) -> clap::error::Result<()> { + if let Some((name, mut matches)) = matches.remove_subcommand() { + let job_kind_registry_snapshot = registry::JobKindRegistrySnapshot::get(); + if let Some(job_kind) = job_kind_registry_snapshot.get_by_name(&name) { + self.update_from_subcommand_arg_matches(job_kind, &mut matches) + } else { + Err(clap::Error::raw( + clap::error::ErrorKind::InvalidSubcommand, + format!("the subcommand '{name}' wasn't recognized"), + )) + } + } else { + Err(clap::Error::raw( + clap::error::ErrorKind::MissingSubcommand, + "a subcommand is required but one was not provided", + )) + } + } +} + +impl RunBuild for AnyJobSubcommand { + fn run_without_platform( + self, + make_params: F, + global_params: &GlobalParams, + ) -> eyre::Result<()> + where + F: FnOnce(Extra) -> eyre::Result, + { + let Self { + args, + dependencies_args, + extra, + } = self; + let params = make_params(extra)?; + let (job, dependencies) = args.args_to_jobs(dependencies_args, ¶ms, global_params)?; + let mut job_graph = JobGraph::new(); + job_graph.add_jobs([job].into_iter().chain(dependencies)); // add all at once to avoid recomputing graph properties multiple times + job_graph.run(¶ms, global_params) + } + fn get_platform(&self) -> Option<&DynPlatform> { + self.args + .base_job_args_dyn(&self.dependencies_args) + .platform + .as_ref() + } +} + +pub fn program_name_for_internal_jobs() -> Interned { + static PROGRAM_NAME: OnceLock> = OnceLock::new(); + *PROGRAM_NAME.get_or_init(|| { + std::env::args_os() + .next() + .expect("can't get program name") + .intern_deref() + }) +} + +#[derive(clap::Args, Debug, Clone, Hash, PartialEq, Eq)] +#[group(id = "BaseJob")] +#[non_exhaustive] +pub struct BaseJobArgs { + /// the directory to put the generated main output file and associated files in + #[arg(short, long, value_hint = clap::ValueHint::DirPath)] + pub output: Option, + #[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")] + pub keep_temp_dir: bool, + /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo + #[arg(long)] + pub file_stem: Option, + /// run commands even if their results are already cached + #[arg(long, env = Self::RUN_EVEN_IF_CACHED_ENV_NAME)] + pub run_even_if_cached: bool, + /// platform + #[arg(long)] + pub platform: Option, +} + +impl BaseJobArgs { + pub const RUN_EVEN_IF_CACHED_ENV_NAME: &'static str = "FAYALITE_RUN_EVEN_IF_CACHED"; + pub fn from_output_dir_and_env(output: PathBuf, platform: Option) -> Self { + Self { + output: Some(output), + keep_temp_dir: false, + file_stem: None, + run_even_if_cached: std::env::var_os(Self::RUN_EVEN_IF_CACHED_ENV_NAME).is_some(), + platform, + } + } +} + +impl ToArgs for BaseJobArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { + output, + keep_temp_dir, + file_stem, + run_even_if_cached, + platform, + } = self; + if let Some(output) = output { + args.write_long_option_eq("output", output); + } + if *keep_temp_dir { + args.write_arg("--keep-temp-dir"); + } + if let Some(file_stem) = file_stem { + args.write_long_option_eq("file-stem", file_stem); + } + if *run_even_if_cached { + args.write_arg("--run-even-if-cached"); + } + if let Some(platform) = platform { + args.write_long_option_eq("platform", platform.name()); + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct BaseJob { + output_dir: Interned, + #[serde(skip)] + temp_dir: Option>, + file_stem: Interned, + run_even_if_cached: bool, + platform: Option, +} + +impl Hash for BaseJob { + fn hash(&self, state: &mut H) { + let Self { + output_dir, + temp_dir: _, + file_stem, + run_even_if_cached, + platform, + } = self; + output_dir.hash(state); + file_stem.hash(state); + run_even_if_cached.hash(state); + platform.hash(state); + } +} + +impl Eq for BaseJob {} + +impl PartialEq for BaseJob { + fn eq(&self, other: &Self) -> bool { + let Self { + output_dir, + temp_dir: _, + file_stem, + run_even_if_cached, + ref platform, + } = *self; + output_dir == other.output_dir + && file_stem == other.file_stem + && run_even_if_cached == other.run_even_if_cached + && *platform == other.platform + } +} + +impl BaseJob { + pub fn output_dir(&self) -> Interned { + self.output_dir + } + pub fn temp_dir(&self) -> Option<&Arc> { + self.temp_dir.as_ref() + } + pub fn file_stem(&self) -> Interned { + self.file_stem + } + pub fn file_with_ext(&self, ext: impl AsRef) -> Interned { + let mut retval = self.output_dir().join(self.file_stem()); + retval.set_extension(ext); + retval.intern_deref() + } + pub fn run_even_if_cached(&self) -> bool { + self.run_even_if_cached + } + pub fn platform(&self) -> Option<&DynPlatform> { + self.platform.as_ref() + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] +pub struct BaseJobKind; + +impl JobKindHelper for BaseJobKind { + fn base_job<'a>( + self, + job: &'a ::Job, + _dependencies: &'a <::Dependencies as JobDependencies>::JobsAndKinds, + ) -> &'a BaseJob { + job + } + fn base_job_args<'a>( + self, + args: &'a ::Args, + _dependencies: &'a <::Dependencies as JobDependencies>::KindsAndArgs, + ) -> &'a BaseJobArgs { + args + } + #[track_caller] + fn base_job_args_dyn<'a>( + self, + args: &'a ::Args, + dependencies_args: &'a [DynJobArgs], + ) -> &'a BaseJobArgs { + let [] = dependencies_args else { + panic!("wrong number of dependencies"); + }; + args + } + #[track_caller] + fn base_job_dyn<'a>( + self, + job: &'a ::Job, + dependencies: &'a [DynJob], + ) -> &'a BaseJob { + let [] = dependencies else { + panic!("wrong number of dependencies"); + }; + job + } +} + +impl JobKind for BaseJobKind { + type Args = BaseJobArgs; + type Job = BaseJob; + type Dependencies = (); + + fn dependencies(self) -> Self::Dependencies { + () + } + + fn args_to_jobs( + args: JobArgsAndDependencies, + params: &JobParams, + _global_params: &GlobalParams, + ) -> eyre::Result> { + let BaseJobArgs { + output, + keep_temp_dir, + file_stem, + run_even_if_cached, + platform, + } = args.args.args; + let (output_dir, temp_dir) = if let Some(output) = output { + (Intern::intern_owned(output), None) + } else { + // we create the temp dir here rather than in run so other + // jobs can have their paths based on the chosen temp dir + let temp_dir = TempDir::new()?; + let output_dir = temp_dir.path().intern(); + let temp_dir = if keep_temp_dir { + // use TempDir::into_path() to no longer automatically delete the temp dir + let temp_dir_path = temp_dir.into_path(); + println!("created temporary directory: {}", temp_dir_path.display()); + None + } else { + Some(Arc::new(temp_dir)) + }; + (output_dir, temp_dir) + }; + let file_stem = file_stem + .map(Intern::intern_deref) + .unwrap_or(params.main_module().name().into()); + Ok(JobAndDependencies { + job: JobAndKind { + kind: BaseJobKind, + job: BaseJob { + output_dir, + temp_dir, + file_stem, + run_even_if_cached, + platform, + }, + }, + dependencies: (), + }) + } + + fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> { + Interned::default() + } + + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::Path { + path: job.output_dir, + }] + .intern_slice() + } + + fn name(self) -> Interned { + "base-job".intern() + } + + fn external_command_params(self, job: &Self::Job) -> Option { + Some(CommandParams { + command_line: [ + "mkdir".intern().into(), + "-p".intern().into(), + "--".intern().into(), + job.output_dir.into(), + ] + .intern_slice(), + current_dir: None, + }) + } + + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + _params: &JobParams, + _global_params: &GlobalParams, + _acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + let [] = inputs else { + panic!("invalid inputs for BaseJob"); + }; + std::fs::create_dir_all(&*job.output_dir)?; + Ok(vec![JobItem::Path { + path: job.output_dir, + }]) + } + + fn subcommand_hidden(self) -> bool { + true + } +} + +pub trait GetJob { + fn get_job(this: &Self) -> &J; +} + +impl> GetJob for &'_ T { + fn get_job(this: &Self) -> &J { + T::get_job(this) + } +} + +impl> GetJob for &'_ mut T { + fn get_job(this: &Self) -> &J { + T::get_job(this) + } +} + +impl> GetJob for Box { + fn get_job(this: &Self) -> &J { + T::get_job(this) + } +} + +pub struct GetJobPositionDependencies(PhantomData); + +impl Default for GetJobPositionDependencies { + fn default() -> Self { + Self(Default::default()) + } +} + +impl fmt::Debug for GetJobPositionDependencies { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GetJobPositionDependencies<{}>", + std::any::type_name::() + ) + } +} + +impl Hash for GetJobPositionDependencies { + fn hash(&self, _state: &mut H) {} +} + +impl Ord for GetJobPositionDependencies { + fn cmp(&self, _other: &Self) -> Ordering { + Ordering::Equal + } +} + +impl PartialOrd for GetJobPositionDependencies { + fn partial_cmp(&self, _other: &Self) -> Option { + Some(Ordering::Equal) + } +} + +impl Eq for GetJobPositionDependencies {} + +impl PartialEq for GetJobPositionDependencies { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl Clone for GetJobPositionDependencies { + fn clone(&self) -> Self { + Self(PhantomData) + } +} + +impl Copy for GetJobPositionDependencies {} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub struct GetJobPositionJob; + +impl>>> + GetJob> for JobAndDependencies +{ + fn get_job(this: &Self) -> &J { + GetJob::get_job(&this.dependencies) + } +} + +impl GetJob for JobAndDependencies { + fn get_job(this: &Self) -> &K::Job { + &this.job.job + } +} + +impl>>> + GetJob> for JobArgsAndDependencies +{ + fn get_job(this: &Self) -> &J { + GetJob::get_job(&this.dependencies) + } +} + +impl GetJob for JobArgsAndDependencies { + fn get_job(this: &Self) -> &K::Args { + &this.args.args + } +} + +impl>> + GetJob> for JobKindAndDependencies +{ + fn get_job(this: &Self) -> &J { + GetJob::get_job(&this.dependencies) + } +} + +impl GetJob for JobKindAndDependencies { + fn get_job(this: &Self) -> &K { + &this.kind + } +} diff --git a/crates/fayalite/src/build/external.rs b/crates/fayalite/src/build/external.rs new file mode 100644 index 00000000..1a904144 --- /dev/null +++ b/crates/fayalite/src/build/external.rs @@ -0,0 +1,1177 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + build::{ + ArgsWriter, CommandParams, GlobalParams, JobAndDependencies, JobAndKind, + JobArgsAndDependencies, JobDependencies, JobDependenciesHasBase, JobItem, JobItemName, + JobKind, JobKindAndArgs, JobParams, ToArgs, WriteArgs, + }, + intern::{Intern, Interned}, + util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8}, +}; +use base64::{Engine, prelude::BASE64_URL_SAFE_NO_PAD}; +use clap::builder::OsStringValueParser; +use eyre::{Context, ensure, eyre}; +use serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{DeserializeOwned, Error}, +}; +use std::{ + borrow::Cow, + collections::BTreeMap, + ffi::{OsStr, OsString}, + fmt, + hash::{Hash, Hasher}, + io::Write, + marker::PhantomData, + path::{Path, PathBuf}, + process::ExitStatus, + sync::OnceLock, +}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum ExternalJobCacheVersion { + /// not used, used to be for `FormalCacheVersion` + V1, + V2, +} + +impl ExternalJobCacheVersion { + pub const CURRENT: Self = Self::V2; +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[non_exhaustive] +pub enum MaybeUtf8 { + Utf8(String), + Binary(Vec), +} + +impl MaybeUtf8 { + pub fn as_bytes(&self) -> &[u8] { + match self { + MaybeUtf8::Utf8(v) => v.as_bytes(), + MaybeUtf8::Binary(v) => v, + } + } + pub fn as_os_str(&self) -> &OsStr { + #![allow(unreachable_code)] + #[cfg(unix)] + { + return std::os::unix::ffi::OsStrExt::from_bytes(self.as_bytes()); + } + #[cfg(target_os = "wasi")] + { + return std::os::wasi::ffi::OsStrExt::from_bytes(self.as_bytes()); + } + // implementing WTF-8 is too much of a pain so don't have a special case for windows + if let Ok(s) = str::from_utf8(self.as_bytes()) { + return OsStr::new(s); + } + panic!("invalid UTF-8 conversion to OsStr is not implemented on this platform"); + } + pub fn as_path(&self) -> &Path { + Path::new(self.as_os_str()) + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename = "MaybeUtf8")] +enum MaybeUtf8Serde<'a> { + Utf8(Cow<'a, str>), + Binary(String), +} + +impl<'de> Deserialize<'de> for MaybeUtf8 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(match MaybeUtf8Serde::deserialize(deserializer)? { + MaybeUtf8Serde::Utf8(v) => Self::Utf8(v.into_owned()), + MaybeUtf8Serde::Binary(v) => BASE64_URL_SAFE_NO_PAD + .decode(&*v) + .map_err(D::Error::custom)? + .into(), + }) + } +} + +impl Serialize for MaybeUtf8 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + MaybeUtf8::Utf8(v) => MaybeUtf8Serde::Utf8(Cow::Borrowed(v)), + MaybeUtf8::Binary(v) => MaybeUtf8Serde::Binary(BASE64_URL_SAFE_NO_PAD.encode(v)), + } + .serialize(serializer) + } +} + +impl From> for MaybeUtf8 { + fn from(value: Vec) -> Self { + match String::from_utf8(value) { + Ok(value) => Self::Utf8(value), + Err(e) => Self::Binary(e.into_bytes()), + } + } +} + +impl From for MaybeUtf8 { + fn from(value: String) -> Self { + Self::Utf8(value) + } +} + +impl From for MaybeUtf8 { + fn from(value: PathBuf) -> Self { + Self::from(value.into_os_string().into_encoded_bytes()) + } +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)] +#[serde(rename = "File")] +pub struct ExternalJobCacheV2File<'a> { + pub name: MaybeUtf8, + pub contents: Cow<'a, MaybeUtf8>, +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct ExternalJobCacheV2Files(pub BTreeMap); + +impl Serialize for ExternalJobCacheV2Files { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq( + self.0 + .iter() + .map(|(name, contents)| ExternalJobCacheV2File { + name: name.clone().into(), + contents: Cow::Borrowed(contents), + }), + ) + } +} + +impl<'de> Deserialize<'de> for ExternalJobCacheV2Files { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self( + Vec::deserialize(deserializer)? + .into_iter() + .map(|ExternalJobCacheV2File { name, contents }| { + (name.as_path().to_path_buf(), contents.into_owned()) + }) + .collect(), + )) + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[serde(rename = "ExternalJobCache")] +pub struct ExternalJobCacheV2 { + pub version: ExternalJobCacheVersion, + pub inputs_hash: blake3::Hash, + pub stdout_stderr: String, + pub result: Result, +} + +impl ExternalJobCacheV2 { + fn read_from_file(cache_json_path: Interned) -> eyre::Result { + let cache_str = std::fs::read_to_string(&*cache_json_path) + .wrap_err_with(|| format!("can't read {cache_json_path:?}"))?; + serde_json::from_str(&cache_str) + .wrap_err_with(|| format!("can't decode {cache_json_path:?}")) + } + fn write_to_file(&self, cache_json_path: Interned) -> eyre::Result<()> { + let cache_str = serde_json::to_string_pretty(&self).expect("serialization can't fail"); + std::fs::write(&*cache_json_path, cache_str) + .wrap_err_with(|| format!("can't write {cache_json_path:?}")) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ExternalJobCaching { + cache_json_path: Interned, + run_even_if_cached: bool, +} + +#[derive(Default)] +struct JobCacheHasher(blake3::Hasher); + +impl JobCacheHasher { + fn hash_size(&mut self, size: usize) { + self.0.update(&u64::to_le_bytes( + size.try_into().expect("size should fit in u64"), + )); + } + fn hash_sized_bytes(&mut self, bytes: &[u8]) { + self.hash_size(bytes.len()); + self.0.update(bytes); + } + fn hash_sized_os_str(&mut self, s: &OsStr) { + self.hash_sized_bytes(s.as_encoded_bytes()); + } + fn hash_iter>( + &mut self, + iter: I, + mut f: F, + ) { + let iter = iter.into_iter(); + self.hash_size(iter.len()); + iter.for_each(|item| f(self, item)); + } + fn try_hash_iter< + F: FnMut(&mut Self, I::Item) -> Result<(), E>, + E, + I: IntoIterator, + >( + &mut self, + iter: I, + mut f: F, + ) -> Result<(), E> { + let mut iter = iter.into_iter(); + self.hash_size(iter.len()); + iter.try_for_each(|item| f(self, item)) + } +} + +fn write_file_atomically_no_clobber C, C: AsRef<[u8]>>( + path: impl AsRef, + containing_dir: impl AsRef, + contents: F, +) -> std::io::Result<()> { + let path = path.as_ref(); + let containing_dir = containing_dir.as_ref(); + if !matches!(std::fs::exists(&path), Ok(true)) { + // use File::create_new rather than tempfile's code to get normal file permissions rather than mode 600 on Unix. + let mut file = tempfile::Builder::new() + .make_in(containing_dir, |path| std::fs::File::create_new(path))?; + file.write_all(contents().as_ref())?; // write all in one operation to avoid a bunch of tiny writes + file.into_temp_path().persist_noclobber(path)?; + } + Ok(()) +} + +impl ExternalJobCaching { + pub fn get_cache_dir_from_output_dir(output_dir: impl AsRef) -> PathBuf { + output_dir.as_ref().join(".fayalite-job-cache") + } + pub fn make_cache_dir( + cache_dir: impl AsRef, + application_name: &str, + ) -> std::io::Result<()> { + let cache_dir = cache_dir.as_ref(); + std::fs::create_dir_all(cache_dir)?; + write_file_atomically_no_clobber(cache_dir.join("CACHEDIR.TAG"), cache_dir, || { + format!( + "Signature: 8a477f597d28d172789f06886806bc55\n\ + # This file is a cache directory tag created by {application_name}.\n\ + # For information about cache directory tags see https://bford.info/cachedir/\n" + ) + })?; + write_file_atomically_no_clobber(cache_dir.join(".gitignore"), cache_dir, || { + format!( + "# This is a cache directory created by {application_name}.\n\ + # ignore all files\n\ + *\n" + ) + }) + } + pub fn new( + output_dir: impl AsRef, + application_name: &str, + json_file_stem: impl AsRef, + run_even_if_cached: bool, + ) -> std::io::Result { + let cache_dir = Self::get_cache_dir_from_output_dir(output_dir); + Self::make_cache_dir(&cache_dir, application_name)?; + let mut cache_json_path = cache_dir; + cache_json_path.push(json_file_stem.as_ref()); + cache_json_path.set_extension("json"); + Ok(Self { + cache_json_path: Path::intern_owned(cache_json_path), + run_even_if_cached, + }) + } + fn write_stdout_stderr(stdout_stderr: &str) { + if stdout_stderr == "" { + return; + } + // use print! so output goes to Rust test output capture + if stdout_stderr.ends_with('\n') { + print!("{stdout_stderr}"); + } else { + println!("{stdout_stderr}"); + } + } + /// returns `Err(_)` if reading the cache failed, otherwise returns `Ok(_)` with the results from the cache + fn run_from_cache( + self, + inputs_hash: blake3::Hash, + output_file_paths: impl IntoIterator>, + ) -> Result, ()> { + if self.run_even_if_cached { + return Err(()); + } + let Ok(ExternalJobCacheV2 { + version: ExternalJobCacheVersion::CURRENT, + inputs_hash: cached_inputs_hash, + stdout_stderr, + result, + }) = ExternalJobCacheV2::read_from_file(self.cache_json_path) + else { + return Err(()); + }; + if inputs_hash != cached_inputs_hash { + return Err(()); + } + match result { + Ok(outputs) => { + for output_file_path in output_file_paths { + let Some(output_data) = outputs.0.get(&*output_file_path) else { + if let Ok(true) = std::fs::exists(&*output_file_path) { + // assume the existing file is the correct one + continue; + } + return Err(()); + }; + let Ok(()) = std::fs::write(&*output_file_path, output_data.as_bytes()) else { + return Err(()); + }; + } + Self::write_stdout_stderr(&stdout_stderr); + Ok(Ok(())) + } + Err(error) => { + Self::write_stdout_stderr(&stdout_stderr); + Ok(Err(error)) + } + } + } + fn make_command( + command_line: Interned<[Interned]>, + ) -> eyre::Result { + ensure!(!command_line.is_empty(), "command line must not be empty"); + let mut cmd = std::process::Command::new(&*command_line[0]); + cmd.args(command_line[1..].iter().map(|arg| &**arg)) + .stdin(std::process::Stdio::null()); + Ok(cmd) + } + pub fn run( + self, + command_line: Interned<[Interned]>, + input_file_paths: impl IntoIterator>, + output_file_paths: impl IntoIterator> + Clone, + run_fn: F, + exit_status_to_error: impl FnOnce(ExitStatus) -> eyre::Report, + ) -> eyre::Result<()> + where + F: FnOnce(std::process::Command) -> eyre::Result>, + { + let mut hasher = JobCacheHasher::default(); + hasher.hash_iter(command_line.iter(), |hasher, arg| { + hasher.hash_sized_os_str(arg) + }); + let mut input_file_paths = + Vec::<&Path>::from_iter(input_file_paths.into_iter().map(Interned::into_inner)); + input_file_paths.sort_unstable(); + input_file_paths.dedup(); + hasher.try_hash_iter( + &input_file_paths, + |hasher, input_file_path| -> eyre::Result<()> { + hasher.hash_sized_os_str(input_file_path.as_ref()); + hasher.hash_sized_bytes( + &std::fs::read(input_file_path).wrap_err_with(|| { + format!("can't read job input file: {input_file_path:?}") + })?, + ); + Ok(()) + }, + )?; + let inputs_hash = hasher.0.finalize(); + match self.run_from_cache(inputs_hash, output_file_paths.clone()) { + Ok(result) => return result.map_err(|e| eyre!(e)), + Err(()) => {} + } + let (pipe_reader, stdout, stderr) = std::io::pipe() + .and_then(|(r, w)| Ok((r, w.try_clone()?, w))) + .wrap_err_with(|| format!("when trying to create a pipe to run: {command_line:?}"))?; + let mut cmd = Self::make_command(command_line)?; + cmd.stdout(stdout).stderr(stderr); + let mut stdout_stderr = String::new(); + let result = std::thread::scope(|scope| { + std::thread::Builder::new() + .name(format!("stdout:{}", command_line[0].display())) + .spawn_scoped(scope, || { + let _ = streaming_read_utf8(std::io::BufReader::new(pipe_reader), |s| { + stdout_stderr.push_str(s); + // use print! so output goes to Rust test output capture + print!("{s}"); + std::io::Result::Ok(()) + }); + if !stdout_stderr.is_empty() && !stdout_stderr.ends_with('\n') { + println!(); + } + }) + .expect("spawn shouldn't fail"); + run_fn(cmd) + })?; + if let Err(exit_status) = result { + // check if the user may have terminated it or something, don't cache the failure + let user_maybe_terminated; + #[cfg(unix)] + { + user_maybe_terminated = std::os::unix::process::ExitStatusExt::signal(&exit_status) + .is_some() + || exit_status.code().is_none_or(|code| code > 1); + } + #[cfg(not(unix))] + { + user_maybe_terminated = !exit_status.success(); + } + if user_maybe_terminated { + let _ = std::fs::remove_file(self.cache_json_path); + return Err(exit_status_to_error(exit_status)); + } + } + let result = result.map_err(exit_status_to_error); + ExternalJobCacheV2 { + version: ExternalJobCacheVersion::CURRENT, + inputs_hash, + stdout_stderr, + result: match &result { + Ok(()) => Ok(ExternalJobCacheV2Files(Result::from_iter( + output_file_paths.into_iter().map( + |output_file_path: Interned| -> eyre::Result<_> { + let output_file_path = &*output_file_path; + Ok(( + PathBuf::from(output_file_path), + MaybeUtf8::from(std::fs::read(output_file_path).wrap_err_with( + || format!("can't read job output file: {output_file_path:?}"), + )?), + )) + }, + ), + )?)), + Err(e) => Err(format!("{e:#}")), + }, + } + .write_to_file(self.cache_json_path)?; + result + } + pub fn run_maybe_cached( + this: Option, + command_line: Interned<[Interned]>, + input_file_paths: impl IntoIterator>, + output_file_paths: impl IntoIterator> + Clone, + run_fn: F, + exit_status_to_error: impl FnOnce(ExitStatus) -> eyre::Report, + ) -> eyre::Result<()> + where + F: FnOnce(std::process::Command) -> eyre::Result>, + { + match this { + Some(this) => this.run( + command_line, + input_file_paths, + output_file_paths, + run_fn, + exit_status_to_error, + ), + None => run_fn(Self::make_command(command_line)?)?.map_err(exit_status_to_error), + } + } +} + +#[derive(Clone, Eq, Hash)] +pub struct ExternalCommandJobKind(PhantomData); + +impl fmt::Debug for ExternalCommandJobKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ExternalCommandJobKind<{}>", std::any::type_name::()) + } +} + +impl PartialEq for ExternalCommandJobKind { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl Ord for ExternalCommandJobKind { + fn cmp(&self, _other: &Self) -> std::cmp::Ordering { + std::cmp::Ordering::Equal + } +} + +impl PartialOrd for ExternalCommandJobKind { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Default for ExternalCommandJobKind { + fn default() -> Self { + Self(PhantomData) + } +} + +impl Copy for ExternalCommandJobKind {} + +impl ExternalCommandJobKind { + pub const fn new() -> Self { + Self(PhantomData) + } +} + +#[derive(Copy, Clone)] +struct ExternalProgramPathValueParser(ExternalProgram); + +fn parse_which_result( + which_result: which::Result, + program_name: impl Into, + program_path_arg_name: impl FnOnce() -> String, +) -> Result, ResolveProgramPathError> { + let which_result = match which_result { + Ok(v) => v, + Err(inner) => { + return Err(ResolveProgramPathError { + inner, + program_name: program_name.into(), + program_path_arg_name: program_path_arg_name(), + }); + } + }; + Ok(which_result.intern_deref()) +} + +impl clap::builder::TypedValueParser for ExternalProgramPathValueParser { + type Value = Interned; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &OsStr, + ) -> clap::error::Result { + let program_path_arg_name = self.0.program_path_arg_name; + OsStringValueParser::new() + .try_map(move |program_name| { + parse_which_result(which::which(&program_name), program_name, || { + program_path_arg_name.into() + }) + }) + .parse_ref(cmd, arg, value) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] +#[group(id = T::args_group_id())] +#[non_exhaustive] +pub struct ExternalCommandArgs { + #[command(flatten)] + pub program_path: ExternalProgramPath, + #[arg( + name = Interned::into_inner(T::run_even_if_cached_arg_name()), + long = T::run_even_if_cached_arg_name(), + )] + pub run_even_if_cached: bool, + #[command(flatten)] + pub additional_args: T::AdditionalArgs, +} + +#[derive(Clone, Debug)] +pub struct ResolveProgramPathError { + inner: which::Error, + program_name: OsString, + program_path_arg_name: String, +} + +impl fmt::Display for ResolveProgramPathError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + inner, + program_name, + program_path_arg_name, + } = self; + write!( + f, + "{program_path_arg_name}: failed to resolve {program_name:?} to a valid program: {inner}", + ) + } +} + +impl std::error::Error for ResolveProgramPathError {} + +pub fn resolve_program_path( + program_name: Option<&OsStr>, + default_program_name: impl AsRef, + program_path_env_var_name: Option<&OsStr>, +) -> Result, ResolveProgramPathError> { + let default_program_name = default_program_name.as_ref(); + let owned_program_name; + let program_name = if let Some(program_name) = program_name { + program_name + } else if let Some(v) = program_path_env_var_name.and_then(std::env::var_os) { + owned_program_name = v; + &owned_program_name + } else { + default_program_name + }; + parse_which_result(which::which(program_name), program_name, || { + default_program_name.display().to_string() + }) +} + +impl ExternalCommandArgs { + pub fn with_resolved_program_path( + program_path: Interned, + additional_args: T::AdditionalArgs, + ) -> Self { + Self::new( + ExternalProgramPath::with_resolved_program_path(program_path), + additional_args, + ) + } + pub fn new( + program_path: ExternalProgramPath, + additional_args: T::AdditionalArgs, + ) -> Self { + Self { + program_path, + run_even_if_cached: false, + additional_args, + } + } + pub fn resolve_program_path( + program_name: Option<&OsStr>, + additional_args: T::AdditionalArgs, + ) -> Result { + Ok(Self::new( + ExternalProgramPath::resolve_program_path(program_name)?, + additional_args, + )) + } +} + +impl ToArgs for ExternalCommandArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { + program_path, + run_even_if_cached, + ref additional_args, + } = *self; + program_path.to_args(args); + if run_even_if_cached { + args.write_display_arg(format_args!("--{}", T::run_even_if_cached_arg_name())); + } + additional_args.to_args(args); + } +} + +#[derive(Copy, Clone)] +struct ExternalCommandJobParams { + command_params: CommandParams, + inputs: Interned<[JobItemName]>, + outputs: Interned<[JobItemName]>, + output_paths: Interned<[Interned]>, +} + +impl ExternalCommandJobParams { + fn new(job: &ExternalCommandJob) -> Self { + let output_paths = T::output_paths(job); + let mut command_line = ArgsWriter(vec![job.program_path.as_interned_os_str()]); + T::command_line_args(job, &mut command_line); + Self { + command_params: CommandParams { + command_line: Intern::intern_owned(command_line.0), + current_dir: T::current_dir(job), + }, + inputs: T::inputs(job), + outputs: output_paths + .iter() + .map(|&path| JobItemName::Path { path }) + .collect(), + output_paths, + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct ExternalCommandJob { + additional_job_data: T::AdditionalJobData, + program_path: Interned, + output_dir: Interned, + run_even_if_cached: bool, + #[serde(skip)] + params_cache: OnceLock, +} + +impl Eq for ExternalCommandJob {} + +impl> Clone for ExternalCommandJob { + fn clone(&self) -> Self { + let Self { + ref additional_job_data, + program_path, + output_dir, + run_even_if_cached, + ref params_cache, + } = *self; + Self { + additional_job_data: additional_job_data.clone(), + program_path, + output_dir, + run_even_if_cached, + params_cache: params_cache.clone(), + } + } +} + +impl fmt::Debug for ExternalCommandJob { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + additional_job_data, + program_path, + output_dir, + run_even_if_cached, + params_cache: _, + } = self; + write!(f, "ExternalCommandJob<{}>", std::any::type_name::())?; + f.debug_struct("") + .field("additional_job_data", additional_job_data) + .field("program_path", program_path) + .field("output_dir", output_dir) + .field("run_even_if_cached", run_even_if_cached) + .finish() + } +} + +impl PartialEq for ExternalCommandJob { + fn eq(&self, other: &Self) -> bool { + let Self { + additional_job_data, + program_path, + output_dir, + run_even_if_cached, + params_cache: _, + } = self; + *additional_job_data == other.additional_job_data + && *program_path == other.program_path + && *output_dir == other.output_dir + && *run_even_if_cached == other.run_even_if_cached + } +} + +impl Hash for ExternalCommandJob { + fn hash(&self, state: &mut H) { + let Self { + additional_job_data, + program_path, + output_dir, + run_even_if_cached, + params_cache: _, + } = self; + additional_job_data.hash(state); + program_path.hash(state); + output_dir.hash(state); + run_even_if_cached.hash(state); + } +} + +impl ExternalCommandJob { + pub fn additional_job_data(&self) -> &T::AdditionalJobData { + &self.additional_job_data + } + pub fn program_path(&self) -> Interned { + self.program_path + } + pub fn output_dir(&self) -> Interned { + self.output_dir + } + pub fn run_even_if_cached(&self) -> bool { + self.run_even_if_cached + } + fn params(&self) -> &ExternalCommandJobParams { + self.params_cache + .get_or_init(|| ExternalCommandJobParams::new(self)) + } + pub fn command_params(&self) -> CommandParams { + self.params().command_params + } + pub fn inputs(&self) -> Interned<[JobItemName]> { + self.params().inputs + } + pub fn output_paths(&self) -> Interned<[Interned]> { + self.params().output_paths + } + pub fn outputs(&self) -> Interned<[JobItemName]> { + self.params().outputs + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct ExternalProgramPath { + program_path: Interned, + _phantom: PhantomData, +} + +impl ExternalProgramPath { + pub fn with_resolved_program_path(program_path: Interned) -> Self { + Self { + program_path, + _phantom: PhantomData, + } + } + pub fn resolve_program_path( + program_name: Option<&OsStr>, + ) -> Result { + let ExternalProgram { + default_program_name, + program_path_arg_name: _, + program_path_arg_value_name: _, + program_path_env_var_name, + } = ExternalProgram::new::(); + Ok(Self { + program_path: resolve_program_path( + program_name, + default_program_name, + program_path_env_var_name.as_ref().map(OsStr::new), + )?, + _phantom: PhantomData, + }) + } + pub fn program_path(&self) -> Interned { + self.program_path + } +} + +impl fmt::Debug for ExternalProgramPath { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + program_path, + _phantom: _, + } = self; + write!(f, "ExternalProgramPath<{}>", std::any::type_name::())?; + f.debug_tuple("").field(program_path).finish() + } +} + +impl clap::FromArgMatches for ExternalProgramPath { + fn from_arg_matches(matches: &clap::ArgMatches) -> Result { + let id = Interned::into_inner(ExternalProgram::new::().program_path_arg_name); + // don't remove argument so later instances of Self can use it too + let program_path = *matches.get_one(id).expect("arg should always be present"); + Ok(Self { + program_path, + _phantom: PhantomData, + }) + } + + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { + *self = Self::from_arg_matches(matches)?; + Ok(()) + } +} + +impl clap::Args for ExternalProgramPath { + fn augment_args(cmd: clap::Command) -> clap::Command { + let external_program @ ExternalProgram { + default_program_name, + program_path_arg_name, + program_path_arg_value_name, + program_path_env_var_name, + } = ExternalProgram::new::(); + let arg = cmd + .get_arguments() + .find(|arg| *arg.get_id().as_str() == *program_path_arg_name); + if let Some(arg) = arg { + // don't insert duplicate arguments. + // check that the previous argument actually matches this argument: + assert!(!arg.is_required_set()); + assert!(matches!(arg.get_action(), clap::ArgAction::Set)); + assert_eq!(arg.get_long(), Some(&*program_path_arg_name)); + assert_eq!( + arg.get_value_names(), + Some(&[clap::builder::Str::from(program_path_arg_value_name)][..]) + ); + assert_eq!( + arg.get_env(), + program_path_env_var_name.as_ref().map(OsStr::new) + ); + assert_eq!( + arg.get_default_values(), + &[OsStr::new(&default_program_name)] + ); + assert_eq!(arg.get_value_hint(), clap::ValueHint::CommandName); + cmd + } else { + cmd.arg( + clap::Arg::new(Interned::into_inner(program_path_arg_name)) + .required(false) + .value_parser(ExternalProgramPathValueParser(external_program)) + .action(clap::ArgAction::Set) + .long(program_path_arg_name) + .value_name(program_path_arg_value_name) + .env(program_path_env_var_name.map(Interned::into_inner)) + .default_value(default_program_name) + .value_hint(clap::ValueHint::CommandName), + ) + } + } + + fn augment_args_for_update(cmd: clap::Command) -> clap::Command { + Self::augment_args(cmd) + } +} + +impl ToArgs for ExternalProgramPath { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let ExternalProgram { + program_path_arg_name, + .. + } = ExternalProgram::new::(); + let Self { + program_path, + _phantom: _, + } = self; + if args.get_long_option_eq(program_path_arg_name) != Some(program_path.as_os_str()) { + args.write_long_option_eq(program_path_arg_name, program_path); + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[non_exhaustive] +pub struct ExternalProgram { + default_program_name: Interned, + program_path_arg_name: Interned, + program_path_arg_value_name: Interned, + program_path_env_var_name: Option>, +} + +impl ExternalProgram { + pub fn new() -> Self { + Self { + default_program_name: T::default_program_name(), + program_path_arg_name: T::program_path_arg_name(), + program_path_arg_value_name: T::program_path_arg_value_name(), + program_path_env_var_name: T::program_path_env_var_name(), + } + } + pub fn default_program_name(&self) -> Interned { + self.default_program_name + } + pub fn program_path_arg_name(&self) -> Interned { + self.program_path_arg_name + } + pub fn program_path_arg_value_name(&self) -> Interned { + self.program_path_arg_value_name + } + pub fn program_path_env_var_name(&self) -> Option> { + self.program_path_env_var_name + } +} + +impl From for ExternalProgram { + fn from(_value: T) -> Self { + Self::new::() + } +} + +impl From for Interned { + fn from(_value: T) -> Self { + ExternalProgram::new::().intern_sized() + } +} + +pub trait ExternalProgramTrait: + 'static + Send + Sync + Hash + Ord + fmt::Debug + Default + Copy +{ + fn program_path_arg_name() -> Interned { + Self::default_program_name() + } + fn program_path_arg_value_name() -> Interned { + Intern::intern_owned(Self::program_path_arg_name().to_uppercase()) + } + fn default_program_name() -> Interned; + fn program_path_env_var_name() -> Option> { + Some(Intern::intern_owned( + Self::program_path_arg_name() + .to_uppercase() + .replace('-', "_"), + )) + } +} + +pub trait ExternalCommand: 'static + Send + Sync + Hash + Eq + fmt::Debug + Sized + Clone { + type AdditionalArgs: ToArgs; + type AdditionalJobData: 'static + + Send + + Sync + + Hash + + Eq + + fmt::Debug + + Serialize + + DeserializeOwned; + type BaseJobPosition; + type Dependencies: JobDependenciesHasBase; + type ExternalProgram: ExternalProgramTrait; + fn dependencies() -> Self::Dependencies; + fn args_to_jobs( + args: JobArgsAndDependencies>, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<( + Self::AdditionalJobData, + ::JobsAndKinds, + )>; + fn inputs(job: &ExternalCommandJob) -> Interned<[JobItemName]>; + fn output_paths(job: &ExternalCommandJob) -> Interned<[Interned]>; + fn command_line_args(job: &ExternalCommandJob, args: &mut W); + fn current_dir(job: &ExternalCommandJob) -> Option>; + fn job_kind_name() -> Interned; + fn args_group_id() -> clap::Id { + Interned::into_inner(Self::job_kind_name()).into() + } + fn run_even_if_cached_arg_name() -> Interned { + Intern::intern_owned(format!("{}-run-even-if-cached", Self::job_kind_name())) + } + fn subcommand_hidden() -> bool { + false + } +} + +impl JobKind for ExternalCommandJobKind { + type Args = ExternalCommandArgs; + type Job = ExternalCommandJob; + type Dependencies = T::Dependencies; + + fn dependencies(self) -> Self::Dependencies { + T::dependencies() + } + + fn args_to_jobs( + args: JobArgsAndDependencies, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result> { + let JobKindAndArgs { + kind, + args: + ExternalCommandArgs { + program_path: + ExternalProgramPath { + program_path, + _phantom: _, + }, + run_even_if_cached, + additional_args: _, + }, + } = args.args; + let (additional_job_data, dependencies) = T::args_to_jobs(args, params, global_params)?; + let base_job = T::Dependencies::base_job(&dependencies); + let job = ExternalCommandJob { + additional_job_data, + program_path, + output_dir: base_job.output_dir(), + run_even_if_cached: base_job.run_even_if_cached() | run_even_if_cached, + params_cache: OnceLock::new(), + }; + job.params(); // fill cache + Ok(JobAndDependencies { + job: JobAndKind { kind, job }, + dependencies, + }) + } + + fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + job.inputs() + } + + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + job.outputs() + } + + fn name(self) -> Interned { + T::job_kind_name() + } + + fn external_command_params(self, job: &Self::Job) -> Option { + Some(job.command_params()) + } + + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + _params: &JobParams, + global_params: &GlobalParams, + acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + assert!( + inputs.iter().map(JobItem::name).eq(job.inputs()), + "{}\ninputs:\n{inputs:?}\njob.inputs():\n{:?}", + std::any::type_name::(), + job.inputs(), + ); + let CommandParams { + command_line, + current_dir, + } = job.command_params(); + ExternalJobCaching::new( + &job.output_dir, + &global_params.application_name(), + &T::job_kind_name(), + job.run_even_if_cached, + )? + .run( + command_line, + inputs + .iter() + .flat_map(|item| match item { + JobItem::Path { path } => std::slice::from_ref(path), + JobItem::DynamicPaths { + paths, + source_job_name: _, + } => paths, + }) + .copied(), + job.output_paths(), + |mut cmd| { + if let Some(current_dir) = current_dir { + cmd.current_dir(current_dir); + } + let status = acquired_job.run_command(cmd, |cmd| cmd.status())?; + if !status.success() { + Ok(Err(status)) + } else { + Ok(Ok(())) + } + }, + |status| eyre!("running {command_line:?} failed: {status}"), + )?; + Ok(job + .output_paths() + .iter() + .map(|&path| JobItem::Path { path }) + .collect()) + } + + fn subcommand_hidden(self) -> bool { + T::subcommand_hidden() + } + + fn external_program(self) -> Option> { + Some(ExternalProgram::new::().intern_sized()) + } +} diff --git a/crates/fayalite/src/build/firrtl.rs b/crates/fayalite/src/build/firrtl.rs new file mode 100644 index 00000000..b5574a93 --- /dev/null +++ b/crates/fayalite/src/build/firrtl.rs @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + build::{ + BaseJob, BaseJobKind, CommandParams, DynJobKind, GlobalParams, JobAndDependencies, + JobArgsAndDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, + ToArgs, WriteArgs, + }, + firrtl::{ExportOptions, FileBackend}, + intern::{Intern, InternSlice, Interned}, + util::job_server::AcquiredJob, +}; +use clap::Args; +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] +pub struct FirrtlJobKind; + +#[derive(Args, Debug, Clone, Hash, PartialEq, Eq)] +#[group(id = "Firrtl")] +#[non_exhaustive] +pub struct FirrtlArgs { + #[command(flatten)] + pub export_options: ExportOptions, +} + +impl ToArgs for FirrtlArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { export_options } = self; + export_options.to_args(args); + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Firrtl { + base: BaseJob, + export_options: ExportOptions, +} + +impl Firrtl { + fn make_firrtl_file_backend(&self) -> FileBackend { + FileBackend { + dir_path: PathBuf::from(&*self.base.output_dir()), + top_fir_file_stem: Some(self.base.file_stem().into()), + circuit_name: None, + } + } + pub fn firrtl_file(&self) -> Interned { + self.base.file_with_ext("fir") + } +} + +impl JobKind for FirrtlJobKind { + type Args = FirrtlArgs; + type Job = Firrtl; + type Dependencies = JobKindAndDependencies; + + fn dependencies(self) -> Self::Dependencies { + JobKindAndDependencies::new(BaseJobKind) + } + + fn args_to_jobs( + args: JobArgsAndDependencies, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result> { + args.args_to_jobs_simple( + params, + global_params, + |_kind, FirrtlArgs { export_options }, dependencies| { + Ok(Firrtl { + base: dependencies.get_job::().clone(), + export_options, + }) + }, + ) + } + + fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::Path { + path: job.base.output_dir(), + }] + .intern_slice() + } + + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::Path { + path: job.firrtl_file(), + }] + .intern_slice() + } + + fn name(self) -> Interned { + "firrtl".intern() + } + + fn external_command_params(self, _job: &Self::Job) -> Option { + None + } + + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + params: &JobParams, + _global_params: &GlobalParams, + _acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + let [JobItem::Path { path: input_path }] = *inputs else { + panic!("wrong inputs, expected a single `Path`"); + }; + assert_eq!(input_path, job.base.output_dir()); + crate::firrtl::export( + job.make_firrtl_file_backend(), + params.main_module(), + job.export_options, + )?; + Ok(vec![JobItem::Path { + path: job.firrtl_file(), + }]) + } +} + +pub(crate) fn built_in_job_kinds() -> impl IntoIterator { + [DynJobKind::new(FirrtlJobKind)] +} diff --git a/crates/fayalite/src/build/formal.rs b/crates/fayalite/src/build/formal.rs new file mode 100644 index 00000000..69c0f2c7 --- /dev/null +++ b/crates/fayalite/src/build/formal.rs @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + build::{ + BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams, + JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, + JobKindAndDependencies, JobParams, ToArgs, WriteArgs, + external::{ + ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, + }, + verilog::{UnadjustedVerilog, VerilogDialect, VerilogJob, VerilogJobKind}, + }, + intern::{Intern, InternSlice, Interned}, + module::NameId, + testing::FormalMode, + util::job_server::AcquiredJob, +}; +use clap::Args; +use eyre::Context; +use serde::{Deserialize, Serialize}; +use std::{ + ffi::{OsStr, OsString}, + fmt::{self, Write}, + path::Path, +}; + +#[derive(Args, Clone, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub struct FormalArgs { + #[arg(long = "sby-extra-arg", value_name = "ARG")] + pub sby_extra_args: Vec, + #[arg(long, default_value_t)] + pub formal_mode: FormalMode, + #[arg(long, default_value_t = Self::DEFAULT_DEPTH)] + pub formal_depth: u64, + #[arg(long, default_value = Self::DEFAULT_SOLVER)] + pub formal_solver: String, + #[arg(long = "smtbmc-extra-arg", value_name = "ARG")] + pub smtbmc_extra_args: Vec, +} + +impl FormalArgs { + pub const DEFAULT_DEPTH: u64 = 20; + pub const DEFAULT_SOLVER: &'static str = "z3"; +} + +impl ToArgs for FormalArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { + sby_extra_args, + formal_mode, + formal_depth, + formal_solver, + smtbmc_extra_args, + } = self; + for arg in sby_extra_args { + args.write_long_option_eq("sby-extra-arg", arg); + } + args.write_display_args([ + format_args!("--formal-mode={formal_mode}"), + format_args!("--formal-depth={formal_depth}"), + format_args!("--formal-solver={formal_solver}"), + ]); + for arg in smtbmc_extra_args { + args.write_long_option_eq("smtbmc-extra-arg", arg); + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub struct WriteSbyFileJobKind; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub struct WriteSbyFileJob { + sby_extra_args: Interned<[Interned]>, + formal_mode: FormalMode, + formal_depth: u64, + formal_solver: Interned, + smtbmc_extra_args: Interned<[Interned]>, + sby_file: Interned, + output_dir: Interned, + main_verilog_file: Interned, +} + +impl WriteSbyFileJob { + pub fn sby_extra_args(&self) -> Interned<[Interned]> { + self.sby_extra_args + } + pub fn formal_mode(&self) -> FormalMode { + self.formal_mode + } + pub fn formal_depth(&self) -> u64 { + self.formal_depth + } + pub fn formal_solver(&self) -> Interned { + self.formal_solver + } + pub fn smtbmc_extra_args(&self) -> Interned<[Interned]> { + self.smtbmc_extra_args + } + pub fn sby_file(&self) -> Interned { + self.sby_file + } + pub fn output_dir(&self) -> Interned { + self.output_dir + } + pub fn main_verilog_file(&self) -> Interned { + self.main_verilog_file + } + fn write_sby( + &self, + output: &mut OsString, + additional_files: &[Interned], + main_module_name_id: NameId, + ) -> eyre::Result<()> { + let Self { + sby_extra_args: _, + formal_mode, + formal_depth, + formal_solver, + smtbmc_extra_args, + sby_file: _, + output_dir: _, + main_verilog_file, + } = self; + write!( + output, + "[options]\n\ + mode {formal_mode}\n\ + depth {formal_depth}\n\ + wait on\n\ + \n\ + [engines]\n\ + smtbmc {formal_solver} -- --" + ) + .expect("writing to OsString can't fail"); + for i in smtbmc_extra_args { + output.push(" "); + output.push(i); + } + output.push( + "\n\ + \n\ + [script]\n", + ); + for verilog_file in VerilogJob::all_verilog_files(*main_verilog_file, additional_files)? { + output.push("read_verilog -sv -formal \""); + output.push(verilog_file); + output.push("\"\n"); + } + let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id); + // workaround for wires disappearing -- set `keep` on all wires + writeln!( + output, + "hierarchy -top {circuit_name}\n\ + proc\n\ + setattr -set keep 1 w:\\*\n\ + prep", + ) + .expect("writing to OsString can't fail"); + Ok(()) + } +} + +impl JobKind for WriteSbyFileJobKind { + type Args = FormalArgs; + type Job = WriteSbyFileJob; + type Dependencies = JobKindAndDependencies; + + fn dependencies(self) -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + mut args: JobArgsAndDependencies, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result> { + args.dependencies + .dependencies + .args + .args + .additional_args + .verilog_dialect + .get_or_insert(VerilogDialect::Yosys); + args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { + let FormalArgs { + sby_extra_args, + formal_mode, + formal_depth, + formal_solver, + smtbmc_extra_args, + } = args; + let base_job = dependencies.get_job::(); + Ok(WriteSbyFileJob { + sby_extra_args: sby_extra_args.into_iter().map(Interned::from).collect(), + formal_mode, + formal_depth, + formal_solver: formal_solver.intern_deref(), + smtbmc_extra_args: smtbmc_extra_args.into_iter().map(Interned::from).collect(), + sby_file: base_job.file_with_ext("sby"), + output_dir: base_job.output_dir(), + main_verilog_file: dependencies.get_job::().main_verilog_file(), + }) + }) + } + + fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::DynamicPaths { + source_job_name: VerilogJobKind.name(), + }] + .intern_slice() + } + + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::Path { path: job.sby_file }].intern_slice() + } + + fn name(self) -> Interned { + "write-sby-file".intern() + } + + fn external_command_params(self, _job: &Self::Job) -> Option { + None + } + + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + params: &JobParams, + _global_params: &GlobalParams, + _acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); + let [additional_files] = inputs else { + unreachable!(); + }; + let additional_files = VerilogJob::unwrap_additional_files(additional_files); + let mut contents = OsString::new(); + job.write_sby( + &mut contents, + additional_files, + params.main_module().name_id(), + )?; + let path = job.sby_file; + std::fs::write(path, contents.as_encoded_bytes()) + .wrap_err_with(|| format!("writing {path:?} failed"))?; + Ok(vec![JobItem::Path { path }]) + } + + fn subcommand_hidden(self) -> bool { + true + } +} + +#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub struct Formal { + #[serde(flatten)] + write_sby_file: WriteSbyFileJob, + sby_file_name: Interned, +} + +impl fmt::Debug for Formal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + write_sby_file: + WriteSbyFileJob { + sby_extra_args, + formal_mode, + formal_depth, + formal_solver, + smtbmc_extra_args, + sby_file, + output_dir: _, + main_verilog_file, + }, + sby_file_name, + } = self; + f.debug_struct("Formal") + .field("sby_extra_args", sby_extra_args) + .field("formal_mode", formal_mode) + .field("formal_depth", formal_depth) + .field("formal_solver", formal_solver) + .field("smtbmc_extra_args", smtbmc_extra_args) + .field("sby_file", sby_file) + .field("sby_file_name", sby_file_name) + .field("main_verilog_file", main_verilog_file) + .finish_non_exhaustive() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub struct Symbiyosys; + +impl ExternalProgramTrait for Symbiyosys { + fn default_program_name() -> Interned { + "sby".intern() + } +} + +#[derive(Clone, Hash, PartialEq, Eq, Debug, Args)] +pub struct FormalAdditionalArgs {} + +impl ToArgs for FormalAdditionalArgs { + fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { + let Self {} = self; + } +} + +impl ExternalCommand for Formal { + type AdditionalArgs = FormalAdditionalArgs; + type AdditionalJobData = Formal; + type BaseJobPosition = GetJobPositionDependencies< + GetJobPositionDependencies< + GetJobPositionDependencies<::BaseJobPosition>, + >, + >; + type Dependencies = JobKindAndDependencies; + type ExternalProgram = Symbiyosys; + + fn dependencies() -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + args: JobArgsAndDependencies>, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<( + Self::AdditionalJobData, + ::JobsAndKinds, + )> { + args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + let FormalAdditionalArgs {} = args.additional_args; + let write_sby_file = dependencies.get_job::().clone(); + Ok(Formal { + sby_file_name: write_sby_file + .sby_file() + .interned_file_name() + .expect("known to have file name"), + write_sby_file, + }) + }) + } + + fn inputs(job: &ExternalCommandJob) -> Interned<[JobItemName]> { + [ + JobItemName::Path { + path: job.additional_job_data().write_sby_file.sby_file(), + }, + JobItemName::Path { + path: job.additional_job_data().write_sby_file.main_verilog_file(), + }, + JobItemName::DynamicPaths { + source_job_name: VerilogJobKind.name(), + }, + ] + .intern_slice() + } + + fn output_paths(_job: &ExternalCommandJob) -> Interned<[Interned]> { + Interned::default() + } + + fn command_line_args(job: &ExternalCommandJob, args: &mut W) { + // args.write_str_arg("-j1"); // sby seems not to respect job count in parallel mode + args.write_arg("-f"); + args.write_interned_arg(job.additional_job_data().sby_file_name); + args.write_interned_args(job.additional_job_data().write_sby_file.sby_extra_args()); + } + + fn current_dir(job: &ExternalCommandJob) -> Option> { + Some(job.output_dir()) + } + + fn job_kind_name() -> Interned { + "formal".intern() + } +} + +pub(crate) fn built_in_job_kinds() -> impl IntoIterator { + [ + DynJobKind::new(WriteSbyFileJobKind), + DynJobKind::new(ExternalCommandJobKind::::new()), + ] +} diff --git a/crates/fayalite/src/build/graph.rs b/crates/fayalite/src/build/graph.rs new file mode 100644 index 00000000..bed88295 --- /dev/null +++ b/crates/fayalite/src/build/graph.rs @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + build::{ + DynJob, GlobalParams, JobItem, JobItemName, JobParams, program_name_for_internal_jobs, + }, + intern::Interned, + platform::DynPlatform, + util::{HashMap, HashSet, job_server::AcquiredJob}, +}; +use eyre::{ContextCompat, eyre}; +use petgraph::{ + algo::{DfsSpace, kosaraju_scc, toposort}, + graph::DiGraph, + visit::{GraphBase, Visitable}, +}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::SerializeSeq}; +use std::{ + cell::OnceCell, + collections::{BTreeMap, BTreeSet, VecDeque}, + convert::Infallible, + ffi::OsStr, + fmt::{self, Write}, + panic, + rc::Rc, + str::Utf8Error, + sync::mpsc, + thread::{self, ScopedJoinHandle}, +}; + +macro_rules! write_str { + ($s:expr, $($rest:tt)*) => { + write!($s, $($rest)*).expect("String::write_fmt can't fail") + }; +} + +#[derive(Clone, Debug)] +enum JobGraphNode { + Job(DynJob), + Item { + #[allow(dead_code, reason = "name used for debugging")] + name: JobItemName, + source_job: Option, + }, +} + +type JobGraphInner = DiGraph; + +#[derive(Clone, Default)] +pub struct JobGraph { + jobs: HashMap::NodeId>, + items: HashMap::NodeId>, + graph: JobGraphInner, + topological_order: Vec<::NodeId>, + space: DfsSpace<::NodeId, ::Map>, +} + +impl fmt::Debug for JobGraph { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + jobs: _, + items: _, + graph, + topological_order, + space: _, + } = self; + f.debug_struct("JobGraph") + .field("graph", graph) + .field("topological_order", topological_order) + .finish_non_exhaustive() + } +} + +#[derive(Clone, Debug)] +pub enum JobGraphError { + CycleError { + job: DynJob, + output: JobItemName, + }, + MultipleJobsCreateSameOutput { + output_item: JobItemName, + existing_job: DynJob, + new_job: DynJob, + }, +} + +impl std::error::Error for JobGraphError {} + +impl fmt::Display for JobGraphError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CycleError { job, output } => write!( + f, + "job can't be added to job graph because it would introduce a cyclic dependency through this job output:\n\ + {output:?}\n\ + job:\n{job:?}", + ), + JobGraphError::MultipleJobsCreateSameOutput { + output_item, + existing_job, + new_job, + } => write!( + f, + "job can't be added to job graph because the new job has an output that is also produced by an existing job.\n\ + conflicting output:\n\ + {output_item:?}\n\ + existing job:\n\ + {existing_job:?}\n\ + new job:\n\ + {new_job:?}", + ), + } + } +} + +#[derive(Copy, Clone, Debug)] +enum EscapeForUnixShellState { + DollarSingleQuote, + SingleQuote, + Unquoted, +} + +#[derive(Clone)] +pub struct EscapeForUnixShell<'a> { + state: EscapeForUnixShellState, + prefix: [u8; 3], + bytes: &'a [u8], +} + +impl<'a> fmt::Debug for EscapeForUnixShell<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl<'a> fmt::Display for EscapeForUnixShell<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for c in self.clone() { + f.write_char(c)?; + } + Ok(()) + } +} + +impl<'a> EscapeForUnixShell<'a> { + pub fn new(s: &'a (impl ?Sized + AsRef)) -> Self { + Self::from_bytes(s.as_ref().as_encoded_bytes()) + } + fn make_prefix(bytes: &[u8]) -> [u8; 3] { + let mut prefix = [0; 3]; + prefix[..bytes.len()].copy_from_slice(bytes); + prefix + } + pub fn from_bytes(bytes: &'a [u8]) -> Self { + let mut needs_single_quote = bytes.is_empty(); + for &b in bytes { + match b { + b'!' | b'\'' | b'\"' | b' ' => needs_single_quote = true, + 0..0x20 | 0x7F.. => { + return Self { + state: EscapeForUnixShellState::DollarSingleQuote, + prefix: Self::make_prefix(b"$'"), + bytes, + }; + } + _ => {} + } + } + if needs_single_quote { + Self { + state: EscapeForUnixShellState::SingleQuote, + prefix: Self::make_prefix(b"'"), + bytes, + } + } else { + Self { + state: EscapeForUnixShellState::Unquoted, + prefix: Self::make_prefix(b""), + bytes, + } + } + } +} + +impl Iterator for EscapeForUnixShell<'_> { + type Item = char; + + fn next(&mut self) -> Option { + match &mut self.prefix { + [0, 0, 0] => {} + [0, 0, v] | // find first + [0, v, _] | // non-zero byte + [v, _, _] => { + let retval = *v as char; + *v = 0; + return Some(retval); + } + } + let Some(&next_byte) = self.bytes.split_off_first() else { + return match self.state { + EscapeForUnixShellState::DollarSingleQuote + | EscapeForUnixShellState::SingleQuote => { + self.state = EscapeForUnixShellState::Unquoted; + Some('\'') + } + EscapeForUnixShellState::Unquoted => None, + }; + }; + match self.state { + EscapeForUnixShellState::DollarSingleQuote => match next_byte { + b'\'' | b'\\' => { + self.prefix = Self::make_prefix(&[next_byte]); + Some('\\') + } + b'\t' => { + self.prefix = Self::make_prefix(b"t"); + Some('\\') + } + b'\n' => { + self.prefix = Self::make_prefix(b"n"); + Some('\\') + } + b'\r' => { + self.prefix = Self::make_prefix(b"r"); + Some('\\') + } + 0x20..=0x7E => Some(next_byte as char), + _ => { + self.prefix = [ + b'x', + char::from_digit(next_byte as u32 >> 4, 0x10).expect("known to be in range") + as u8, + char::from_digit(next_byte as u32 & 0xF, 0x10) + .expect("known to be in range") as u8, + ]; + Some('\\') + } + }, + EscapeForUnixShellState::SingleQuote => { + if next_byte == b'\'' { + self.prefix = Self::make_prefix(b"\\''"); + Some('\'') + } else { + Some(next_byte as char) + } + } + EscapeForUnixShellState::Unquoted => match next_byte { + b' ' | b'!' | b'"' | b'#' | b'$' | b'&' | b'\'' | b'(' | b')' | b'*' | b',' + | b';' | b'<' | b'>' | b'?' | b'[' | b'\\' | b']' | b'^' | b'`' | b'{' | b'|' + | b'}' | b'~' => { + self.prefix = Self::make_prefix(&[next_byte]); + Some('\\') + } + _ => Some(next_byte as char), + }, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[non_exhaustive] +pub enum UnixMakefileEscapeKind { + NonRecipe, + RecipeWithoutShellEscaping, + RecipeWithShellEscaping, +} + +#[derive(Copy, Clone)] +pub struct EscapeForUnixMakefile<'a> { + s: &'a OsStr, + kind: UnixMakefileEscapeKind, +} + +impl<'a> fmt::Debug for EscapeForUnixMakefile<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl<'a> fmt::Display for EscapeForUnixMakefile<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.do_write( + f, + fmt::Write::write_str, + fmt::Write::write_char, + |_, _| Ok(()), + |_| unreachable!("already checked that the input causes no UTF-8 errors"), + ) + } +} + +impl<'a> EscapeForUnixMakefile<'a> { + fn do_write( + &self, + state: &mut S, + write_str: impl Fn(&mut S, &str) -> Result<(), E>, + write_char: impl Fn(&mut S, char) -> Result<(), E>, + add_variable: impl Fn(&mut S, &'static str) -> Result<(), E>, + utf8_error: impl Fn(Utf8Error) -> E, + ) -> Result<(), E> { + let escape_recipe_char = |c| match c { + '$' => write_str(state, "$$"), + '\0'..='\x1F' | '\x7F' => { + panic!("can't escape a control character for Unix Makefile: {c:?}"); + } + _ => write_char(state, c), + }; + match self.kind { + UnixMakefileEscapeKind::NonRecipe => str::from_utf8(self.s.as_encoded_bytes()) + .map_err(&utf8_error)? + .chars() + .try_for_each(|c| match c { + '=' => { + add_variable(state, "EQUALS = =")?; + write_str(state, "$(EQUALS)") + } + ';' => panic!("can't escape a semicolon (;) for Unix Makefile"), + '$' => write_str(state, "$$"), + '\\' | ' ' | '#' | ':' | '%' | '*' | '?' | '[' | ']' | '~' => { + write_char(state, '\\')?; + write_char(state, c) + } + '\0'..='\x1F' | '\x7F' => { + panic!("can't escape a control character for Unix Makefile: {c:?}"); + } + _ => write_char(state, c), + }), + UnixMakefileEscapeKind::RecipeWithoutShellEscaping => { + str::from_utf8(self.s.as_encoded_bytes()) + .map_err(&utf8_error)? + .chars() + .try_for_each(escape_recipe_char) + } + UnixMakefileEscapeKind::RecipeWithShellEscaping => { + EscapeForUnixShell::new(self.s).try_for_each(escape_recipe_char) + } + } + } + pub fn new( + s: &'a (impl ?Sized + AsRef), + kind: UnixMakefileEscapeKind, + needed_variables: &mut BTreeSet<&'static str>, + ) -> Result { + let s = s.as_ref(); + let retval = Self { s, kind }; + retval.do_write( + needed_variables, + |_, _| Ok(()), + |_, _| Ok(()), + |needed_variables, variable| { + needed_variables.insert(variable); + Ok(()) + }, + |e| e, + )?; + Ok(retval) + } +} + +impl JobGraph { + pub fn new() -> Self { + Self::default() + } + fn try_add_item_node( + &mut self, + name: JobItemName, + new_source_job: Option, + new_nodes: &mut HashSet<::NodeId>, + ) -> Result<::NodeId, JobGraphError> { + use hashbrown::hash_map::Entry; + match self.items.entry(name) { + Entry::Occupied(item_entry) => { + let node_id = *item_entry.get(); + let JobGraphNode::Item { + name: _, + source_job, + } = &mut self.graph[node_id] + else { + unreachable!("known to be an item"); + }; + if let Some(new_source_job) = new_source_job { + if let Some(source_job) = source_job { + return Err(JobGraphError::MultipleJobsCreateSameOutput { + output_item: item_entry.key().clone(), + existing_job: source_job.clone(), + new_job: new_source_job, + }); + } else { + *source_job = Some(new_source_job); + } + } + Ok(node_id) + } + Entry::Vacant(item_entry) => { + let node_id = self.graph.add_node(JobGraphNode::Item { + name, + source_job: new_source_job, + }); + new_nodes.insert(node_id); + item_entry.insert(node_id); + Ok(node_id) + } + } + } + pub fn try_add_jobs>( + &mut self, + jobs: I, + ) -> Result<(), JobGraphError> { + use hashbrown::hash_map::Entry; + let jobs = jobs.into_iter(); + struct RemoveNewNodesOnError<'a> { + this: &'a mut JobGraph, + new_nodes: HashSet<::NodeId>, + } + impl Drop for RemoveNewNodesOnError<'_> { + fn drop(&mut self) { + for node in self.new_nodes.drain() { + self.this.graph.remove_node(node); + } + } + } + let mut remove_new_nodes_on_error = RemoveNewNodesOnError { + this: self, + new_nodes: HashSet::with_capacity_and_hasher(jobs.size_hint().0, Default::default()), + }; + let new_nodes = &mut remove_new_nodes_on_error.new_nodes; + let this = &mut *remove_new_nodes_on_error.this; + for job in jobs { + let Entry::Vacant(job_entry) = this.jobs.entry(job.clone()) else { + continue; + }; + let job_node_id = this + .graph + .add_node(JobGraphNode::Job(job_entry.key().clone())); + new_nodes.insert(job_node_id); + job_entry.insert(job_node_id); + for name in job.outputs() { + let item_node_id = this.try_add_item_node(name, Some(job.clone()), new_nodes)?; + this.graph.add_edge(job_node_id, item_node_id, ()); + } + for name in job.inputs() { + let item_node_id = this.try_add_item_node(name, None, new_nodes)?; + this.graph.add_edge(item_node_id, job_node_id, ()); + } + } + match toposort(&this.graph, Some(&mut this.space)) { + Ok(v) => { + this.topological_order = v; + // no need to remove any of the new nodes on drop since we didn't encounter any errors + remove_new_nodes_on_error.new_nodes.clear(); + Ok(()) + } + Err(_) => { + // there's at least one cycle, find one! + let cycle = kosaraju_scc(&this.graph) + .into_iter() + .find_map(|scc| { + if scc.len() <= 1 { + // can't be a cycle since our graph is bipartite -- + // jobs only connect to items, never jobs to jobs or items to items + None + } else { + Some(scc) + } + }) + .expect("we know there's a cycle"); + let cycle_set = HashSet::from_iter(cycle.iter().copied()); + let job = cycle + .into_iter() + .find_map(|node_id| { + if let JobGraphNode::Job(job) = &this.graph[node_id] { + Some(job.clone()) + } else { + None + } + }) + .expect("a job must be part of the cycle"); + let output = job + .outputs() + .into_iter() + .find(|output| cycle_set.contains(&this.items[output])) + .expect("an output must be part of the cycle"); + Err(JobGraphError::CycleError { job, output }) + } + } + } + #[track_caller] + pub fn add_jobs>(&mut self, jobs: I) { + match self.try_add_jobs(jobs) { + Ok(()) => {} + Err(e) => panic!("error: {e}"), + } + } + pub fn to_unix_makefile( + &self, + platform: Option<&DynPlatform>, + extra_args: &[Interned], + ) -> Result { + self.to_unix_makefile_with_internal_program_prefix( + &[program_name_for_internal_jobs()], + platform, + extra_args, + ) + } + pub fn to_unix_makefile_with_internal_program_prefix( + &self, + internal_program_prefix: &[Interned], + platform: Option<&DynPlatform>, + extra_args: &[Interned], + ) -> Result { + let mut retval = String::new(); + let mut needed_variables = BTreeSet::new(); + let mut phony_targets = BTreeSet::new(); + for &node_id in &self.topological_order { + let JobGraphNode::Job(job) = &self.graph[node_id] else { + continue; + }; + let outputs = job.outputs(); + if outputs.is_empty() { + retval.push_str(":"); + } else { + for output in job.outputs() { + match output { + JobItemName::Path { path } => { + write_str!( + retval, + "{} ", + EscapeForUnixMakefile::new( + &str::from_utf8(path.as_os_str().as_encoded_bytes())?, + UnixMakefileEscapeKind::NonRecipe, + &mut needed_variables + )? + ); + } + JobItemName::DynamicPaths { source_job_name } => { + write_str!( + retval, + "{} ", + EscapeForUnixMakefile::new( + &source_job_name, + UnixMakefileEscapeKind::NonRecipe, + &mut needed_variables + )? + ); + phony_targets.insert(Interned::into_inner(source_job_name)); + } + } + } + if outputs.len() == 1 { + retval.push_str(":"); + } else { + retval.push_str("&:"); + } + } + for input in job.inputs() { + match input { + JobItemName::Path { path } => { + write_str!( + retval, + " {}", + EscapeForUnixMakefile::new( + &str::from_utf8(path.as_os_str().as_encoded_bytes())?, + UnixMakefileEscapeKind::NonRecipe, + &mut needed_variables + )? + ); + } + JobItemName::DynamicPaths { source_job_name } => { + write_str!( + retval, + " {}", + EscapeForUnixMakefile::new( + &source_job_name, + UnixMakefileEscapeKind::NonRecipe, + &mut needed_variables + )? + ); + phony_targets.insert(Interned::into_inner(source_job_name)); + } + } + } + retval.push_str("\n\t"); + job.command_params_with_internal_program_prefix( + internal_program_prefix, + platform, + extra_args, + ) + .to_unix_shell_line(&mut retval, |arg, output| { + write_str!( + output, + "{}", + EscapeForUnixMakefile::new( + arg, + UnixMakefileEscapeKind::RecipeWithShellEscaping, + &mut needed_variables + )? + ); + Ok(()) + })?; + retval.push_str("\n\n"); + } + if !phony_targets.is_empty() { + retval.push_str("\n.PHONY:"); + for phony_target in phony_targets { + write_str!( + retval, + " {}", + EscapeForUnixMakefile::new( + phony_target, + UnixMakefileEscapeKind::NonRecipe, + &mut needed_variables + )? + ); + } + retval.push_str("\n"); + } + if !needed_variables.is_empty() { + retval.insert_str( + 0, + &String::from_iter(needed_variables.into_iter().map(|v| format!("{v}\n"))), + ); + } + Ok(retval) + } + pub fn to_unix_shell_script( + &self, + platform: Option<&DynPlatform>, + extra_args: &[Interned], + ) -> String { + self.to_unix_shell_script_with_internal_program_prefix( + &[program_name_for_internal_jobs()], + platform, + extra_args, + ) + } + pub fn to_unix_shell_script_with_internal_program_prefix( + &self, + internal_program_prefix: &[Interned], + platform: Option<&DynPlatform>, + extra_args: &[Interned], + ) -> String { + let mut retval = String::from( + "#!/bin/sh\n\ + set -ex\n", + ); + for &node_id in &self.topological_order { + let JobGraphNode::Job(job) = &self.graph[node_id] else { + continue; + }; + let Ok(()) = job + .command_params_with_internal_program_prefix( + internal_program_prefix, + platform, + extra_args, + ) + .to_unix_shell_line(&mut retval, |arg, output| -> Result<(), Infallible> { + write_str!(output, "{}", EscapeForUnixShell::new(&arg)); + Ok(()) + }); + retval.push_str("\n"); + } + retval + } + pub fn run(&self, params: &JobParams, global_params: &GlobalParams) -> eyre::Result<()> { + // use scope to auto-join threads on errors + thread::scope(|scope| { + struct WaitingJobState { + job_node_id: ::NodeId, + job: DynJob, + inputs: BTreeMap>, + } + let mut ready_jobs = VecDeque::new(); + let mut item_name_to_waiting_jobs_map = HashMap::<_, Vec<_>>::default(); + for &node_id in &self.topological_order { + let JobGraphNode::Job(job) = &self.graph[node_id] else { + continue; + }; + let waiting_job = WaitingJobState { + job_node_id: node_id, + job: job.clone(), + inputs: job + .inputs() + .iter() + .map(|&name| (name, OnceCell::new())) + .collect(), + }; + if waiting_job.inputs.is_empty() { + ready_jobs.push_back(waiting_job); + } else { + let waiting_job = Rc::new(waiting_job); + for &input_item in waiting_job.inputs.keys() { + item_name_to_waiting_jobs_map + .entry(input_item) + .or_default() + .push(waiting_job.clone()); + } + } + } + struct RunningJob<'scope> { + job: DynJob, + thread: ScopedJoinHandle<'scope, eyre::Result>>, + } + let mut running_jobs = HashMap::default(); + let (finished_jobs_sender, finished_jobs_receiver) = mpsc::channel(); + let mut next_finished_job = None; + loop { + if let Some(finished_job) = next_finished_job + .take() + .or_else(|| finished_jobs_receiver.try_recv().ok()) + { + let Some(RunningJob { job, thread }) = running_jobs.remove(&finished_job) + else { + unreachable!(); + }; + let output_items = thread.join().map_err(panic::resume_unwind)??; + assert!( + output_items.iter().map(JobItem::name).eq(job.outputs()), + "job's run() method returned the wrong output items:\n\ + output items:\n\ + {output_items:?}\n\ + expected outputs:\n\ + {:?}\n\ + job:\n\ + {job:?}", + job.outputs(), + ); + for output_item in output_items { + for waiting_job in item_name_to_waiting_jobs_map + .remove(&output_item.name()) + .unwrap_or_default() + { + let Ok(()) = + waiting_job.inputs[&output_item.name()].set(output_item.clone()) + else { + unreachable!(); + }; + if let Some(waiting_job) = Rc::into_inner(waiting_job) { + ready_jobs.push_back(waiting_job); + } + } + } + continue; + } + if let Some(WaitingJobState { + job_node_id, + job, + inputs, + }) = ready_jobs.pop_front() + { + struct RunningJobInThread<'a> { + job_node_id: ::NodeId, + job: DynJob, + inputs: Vec, + params: &'a JobParams, + global_params: &'a GlobalParams, + acquired_job: AcquiredJob, + finished_jobs_sender: mpsc::Sender<::NodeId>, + } + impl RunningJobInThread<'_> { + fn run(mut self) -> eyre::Result> { + self.job.run( + &self.inputs, + self.params, + self.global_params, + &mut self.acquired_job, + ) + } + } + impl Drop for RunningJobInThread<'_> { + fn drop(&mut self) { + let _ = self.finished_jobs_sender.send(self.job_node_id); + } + } + let name = job.kind().name(); + let running_job_in_thread = RunningJobInThread { + job_node_id, + job: job.clone(), + inputs: Result::from_iter(job.inputs().iter().map(|input_name| { + inputs.get(input_name).and_then(|v| v.get().cloned()).wrap_err_with(|| { + eyre!("failed when trying to run job {name}: nothing provided the input item: {input_name:?}") + }) + }))?, + params, + global_params, + acquired_job: AcquiredJob::acquire()?, + finished_jobs_sender: finished_jobs_sender.clone(), + }; + running_jobs.insert( + job_node_id, + RunningJob { + job, + thread: thread::Builder::new() + .name(format!("job:{name}")) + .spawn_scoped(scope, move || running_job_in_thread.run()) + .expect("failed to spawn thread for job"), + }, + ); + continue; + } + if running_jobs.is_empty() { + assert!(item_name_to_waiting_jobs_map.is_empty()); + assert!(ready_jobs.is_empty()); + return Ok(()); + } + // nothing to do yet, block to avoid busy waiting + next_finished_job = finished_jobs_receiver.recv().ok(); + } + }) + } +} + +impl Extend for JobGraph { + #[track_caller] + fn extend>(&mut self, iter: T) { + self.add_jobs(iter); + } +} + +impl FromIterator for JobGraph { + #[track_caller] + fn from_iter>(iter: T) -> Self { + let mut retval = Self::new(); + retval.add_jobs(iter); + retval + } +} + +impl Serialize for JobGraph { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut serializer = serializer.serialize_seq(Some(self.jobs.len()))?; + for &node_id in &self.topological_order { + let JobGraphNode::Job(job) = &self.graph[node_id] else { + continue; + }; + serializer.serialize_element(job)?; + } + serializer.end() + } +} + +impl<'de> Deserialize<'de> for JobGraph { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let jobs = Vec::::deserialize(deserializer)?; + let mut retval = JobGraph::new(); + retval.try_add_jobs(jobs).map_err(D::Error::custom)?; + Ok(retval) + } +} diff --git a/crates/fayalite/src/build/registry.rs b/crates/fayalite/src/build/registry.rs new file mode 100644 index 00000000..bbd9f2c5 --- /dev/null +++ b/crates/fayalite/src/build/registry.rs @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + build::{DynJobKind, JobKind, built_in_job_kinds}, + intern::Interned, + util::InternedStrCompareAsStr, +}; +use std::{ + collections::BTreeMap, + fmt, + sync::{Arc, OnceLock, RwLock, RwLockWriteGuard}, +}; + +impl DynJobKind { + pub fn registry() -> JobKindRegistrySnapshot { + JobKindRegistrySnapshot(JobKindRegistry::get()) + } + #[track_caller] + pub fn register(self) { + JobKindRegistry::register(JobKindRegistry::lock(), self); + } +} + +#[derive(Clone, Debug)] +struct JobKindRegistry { + job_kinds: BTreeMap, +} + +enum JobKindRegisterError { + SameName { + name: InternedStrCompareAsStr, + old_job_kind: DynJobKind, + new_job_kind: DynJobKind, + }, +} + +impl fmt::Display for JobKindRegisterError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SameName { + name, + old_job_kind, + new_job_kind, + } => write!( + f, + "two different `JobKind` can't share the same name:\n\ + {name:?}\n\ + old job kind:\n\ + {old_job_kind:?}\n\ + new job kind:\n\ + {new_job_kind:?}", + ), + } + } +} + +trait JobKindRegistryRegisterLock { + type Locked; + fn lock(self) -> Self::Locked; + fn make_mut(locked: &mut Self::Locked) -> &mut JobKindRegistry; +} + +impl JobKindRegistryRegisterLock for &'static RwLock> { + type Locked = RwLockWriteGuard<'static, Arc>; + fn lock(self) -> Self::Locked { + self.write().expect("shouldn't be poisoned") + } + fn make_mut(locked: &mut Self::Locked) -> &mut JobKindRegistry { + Arc::make_mut(locked) + } +} + +impl JobKindRegistryRegisterLock for &'_ mut JobKindRegistry { + type Locked = Self; + + fn lock(self) -> Self::Locked { + self + } + + fn make_mut(locked: &mut Self::Locked) -> &mut JobKindRegistry { + locked + } +} + +impl JobKindRegistry { + fn lock() -> &'static RwLock> { + static REGISTRY: OnceLock>> = OnceLock::new(); + REGISTRY.get_or_init(Default::default) + } + fn try_register( + lock: L, + job_kind: DynJobKind, + ) -> Result<(), JobKindRegisterError> { + use std::collections::btree_map::Entry; + let name = InternedStrCompareAsStr(job_kind.name()); + // run user code only outside of lock + let mut locked = lock.lock(); + let this = L::make_mut(&mut locked); + let result = match this.job_kinds.entry(name) { + Entry::Occupied(entry) => Err(JobKindRegisterError::SameName { + name, + old_job_kind: entry.get().clone(), + new_job_kind: job_kind, + }), + Entry::Vacant(entry) => { + entry.insert(job_kind); + Ok(()) + } + }; + drop(locked); + // outside of lock now, so we can test if it's the same DynJobKind + match result { + Err(JobKindRegisterError::SameName { + name: _, + old_job_kind, + new_job_kind, + }) if old_job_kind == new_job_kind => Ok(()), + result => result, + } + } + #[track_caller] + fn register(lock: L, job_kind: DynJobKind) { + match Self::try_register(lock, job_kind) { + Err(e) => panic!("{e}"), + Ok(()) => {} + } + } + fn get() -> Arc { + Self::lock().read().expect("shouldn't be poisoned").clone() + } +} + +impl Default for JobKindRegistry { + fn default() -> Self { + let mut retval = Self { + job_kinds: BTreeMap::new(), + }; + for job_kind in built_in_job_kinds() { + Self::register(&mut retval, job_kind); + } + retval + } +} + +#[derive(Clone, Debug)] +pub struct JobKindRegistrySnapshot(Arc); + +impl JobKindRegistrySnapshot { + pub fn get() -> Self { + JobKindRegistrySnapshot(JobKindRegistry::get()) + } + pub fn get_by_name<'a>(&'a self, name: &str) -> Option<&'a DynJobKind> { + self.0.job_kinds.get(name) + } + pub fn iter_with_names(&self) -> JobKindRegistryIterWithNames<'_> { + JobKindRegistryIterWithNames(self.0.job_kinds.iter()) + } + pub fn iter(&self) -> JobKindRegistryIter<'_> { + JobKindRegistryIter(self.0.job_kinds.values()) + } +} + +impl<'a> IntoIterator for &'a JobKindRegistrySnapshot { + type Item = &'a DynJobKind; + type IntoIter = JobKindRegistryIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a> IntoIterator for &'a mut JobKindRegistrySnapshot { + type Item = &'a DynJobKind; + type IntoIter = JobKindRegistryIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[derive(Clone, Debug)] +pub struct JobKindRegistryIter<'a>( + std::collections::btree_map::Values<'a, InternedStrCompareAsStr, DynJobKind>, +); + +impl<'a> Iterator for JobKindRegistryIter<'a> { + type Item = &'a DynJobKind; + + fn next(&mut self) -> Option { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + fn count(self) -> usize + where + Self: Sized, + { + self.0.count() + } + + fn last(self) -> Option { + self.0.last() + } + + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0.fold(init, f) + } +} + +impl<'a> std::iter::FusedIterator for JobKindRegistryIter<'a> {} + +impl<'a> ExactSizeIterator for JobKindRegistryIter<'a> {} + +impl<'a> DoubleEndedIterator for JobKindRegistryIter<'a> { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + + fn nth_back(&mut self, n: usize) -> Option { + self.0.nth_back(n) + } + + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0.rfold(init, f) + } +} + +#[derive(Clone, Debug)] +pub struct JobKindRegistryIterWithNames<'a>( + std::collections::btree_map::Iter<'a, InternedStrCompareAsStr, DynJobKind>, +); + +impl<'a> Iterator for JobKindRegistryIterWithNames<'a> { + type Item = (Interned, &'a DynJobKind); + + fn next(&mut self) -> Option { + self.0.next().map(|(name, job_kind)| (name.0, job_kind)) + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + fn count(self) -> usize + where + Self: Sized, + { + self.0.count() + } + + fn last(self) -> Option { + self.0.last().map(|(name, job_kind)| (name.0, job_kind)) + } + + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n).map(|(name, job_kind)| (name.0, job_kind)) + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0 + .map(|(name, job_kind)| (name.0, job_kind)) + .fold(init, f) + } +} + +impl<'a> std::iter::FusedIterator for JobKindRegistryIterWithNames<'a> {} + +impl<'a> ExactSizeIterator for JobKindRegistryIterWithNames<'a> {} + +impl<'a> DoubleEndedIterator for JobKindRegistryIterWithNames<'a> { + fn next_back(&mut self) -> Option { + self.0 + .next_back() + .map(|(name, job_kind)| (name.0, job_kind)) + } + + fn nth_back(&mut self, n: usize) -> Option { + self.0 + .nth_back(n) + .map(|(name, job_kind)| (name.0, job_kind)) + } + + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0 + .map(|(name, job_kind)| (name.0, job_kind)) + .rfold(init, f) + } +} + +#[track_caller] +pub fn register_job_kind(kind: K) { + DynJobKind::new(kind).register(); +} diff --git a/crates/fayalite/src/build/verilog.rs b/crates/fayalite/src/build/verilog.rs new file mode 100644 index 00000000..7ce77ecb --- /dev/null +++ b/crates/fayalite/src/build/verilog.rs @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + build::{ + BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GetJobPositionJob, + GlobalParams, JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, + JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs, + external::{ + ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, + }, + firrtl::{Firrtl, FirrtlJobKind}, + }, + intern::{Intern, InternSlice, Interned}, + util::job_server::AcquiredJob, +}; +use clap::Args; +use eyre::{Context, bail}; +use serde::{Deserialize, Serialize}; +use std::{ + ffi::{OsStr, OsString}, + fmt, mem, + path::Path, +}; + +/// based on [LLVM Circt's recommended lowering options][lowering-options] +/// +/// [lowering-options]: https://circt.llvm.org/docs/VerilogGeneration/#recommended-loweringoptions-by-target +#[derive(clap::ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] +pub enum VerilogDialect { + Questa, + Spyglass, + Verilator, + Vivado, + Yosys, +} + +impl fmt::Display for VerilogDialect { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl VerilogDialect { + pub fn as_str(self) -> &'static str { + match self { + VerilogDialect::Questa => "questa", + VerilogDialect::Spyglass => "spyglass", + VerilogDialect::Verilator => "verilator", + VerilogDialect::Vivado => "vivado", + VerilogDialect::Yosys => "yosys", + } + } + pub fn firtool_extra_args(self) -> &'static [&'static str] { + match self { + VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"], + VerilogDialect::Spyglass => { + &["--lowering-options=explicitBitcast,disallowExpressionInliningInPorts"] + } + VerilogDialect::Verilator => &[ + "--lowering-options=locationInfoStyle=wrapInAtSquareBracket,disallowLocalVariables", + ], + VerilogDialect::Vivado => &["--lowering-options=mitigateVivadoArrayIndexConstPropBug"], + VerilogDialect::Yosys => { + &["--lowering-options=disallowLocalVariables,disallowPackedArrays"] + } + } + } +} + +#[derive(Args, Debug, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub struct UnadjustedVerilogArgs { + #[arg(long = "firtool-extra-arg", value_name = "ARG")] + pub firtool_extra_args: Vec, + /// adapt the generated Verilog for a particular toolchain + #[arg(long)] + pub verilog_dialect: Option, + #[arg(long)] + pub verilog_debug: bool, +} + +impl ToArgs for UnadjustedVerilogArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { + ref firtool_extra_args, + verilog_dialect, + verilog_debug, + } = *self; + for arg in firtool_extra_args { + args.write_long_option_eq("firtool-extra-arg", arg); + } + if let Some(verilog_dialect) = verilog_dialect { + args.write_long_option_eq("verilog-dialect", verilog_dialect.as_str()); + } + if verilog_debug { + args.write_arg("--verilog-debug"); + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub struct Firtool; + +impl ExternalProgramTrait for Firtool { + fn default_program_name() -> Interned { + "firtool".intern() + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Deserialize, Serialize)] +pub struct UnadjustedVerilog { + firrtl_file: Interned, + firrtl_file_name: Interned, + unadjusted_verilog_file: Interned, + unadjusted_verilog_file_name: Interned, + firtool_extra_args: Interned<[Interned]>, + verilog_dialect: Option, + verilog_debug: bool, +} + +impl UnadjustedVerilog { + pub fn firrtl_file(&self) -> Interned { + self.firrtl_file + } + pub fn unadjusted_verilog_file(&self) -> Interned { + self.unadjusted_verilog_file + } + pub fn firtool_extra_args(&self) -> Interned<[Interned]> { + self.firtool_extra_args + } + pub fn verilog_dialect(&self) -> Option { + self.verilog_dialect + } + pub fn verilog_debug(&self) -> bool { + self.verilog_debug + } +} + +impl ExternalCommand for UnadjustedVerilog { + type AdditionalArgs = UnadjustedVerilogArgs; + type AdditionalJobData = UnadjustedVerilog; + type BaseJobPosition = GetJobPositionDependencies; + type Dependencies = JobKindAndDependencies; + type ExternalProgram = Firtool; + + fn dependencies() -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + args: JobArgsAndDependencies>, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<( + Self::AdditionalJobData, + ::JobsAndKinds, + )> { + args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + let UnadjustedVerilogArgs { + firtool_extra_args, + verilog_dialect, + verilog_debug, + } = args.additional_args; + let unadjusted_verilog_file = dependencies + .dependencies + .job + .job + .file_with_ext("unadjusted.v"); + let firrtl_job = dependencies.get_job::(); + Ok(UnadjustedVerilog { + firrtl_file: firrtl_job.firrtl_file(), + firrtl_file_name: firrtl_job + .firrtl_file() + .interned_file_name() + .expect("known to have file name"), + unadjusted_verilog_file, + unadjusted_verilog_file_name: unadjusted_verilog_file + .interned_file_name() + .expect("known to have file name"), + firtool_extra_args: firtool_extra_args.into_iter().map(Interned::from).collect(), + verilog_dialect, + verilog_debug, + }) + }) + } + + fn inputs(job: &ExternalCommandJob) -> Interned<[JobItemName]> { + [JobItemName::Path { + path: job.additional_job_data().firrtl_file, + }] + .intern_slice() + } + + fn output_paths(job: &ExternalCommandJob) -> Interned<[Interned]> { + [job.additional_job_data().unadjusted_verilog_file].intern_slice() + } + + fn command_line_args(job: &ExternalCommandJob, args: &mut W) { + let UnadjustedVerilog { + firrtl_file: _, + firrtl_file_name, + unadjusted_verilog_file: _, + unadjusted_verilog_file_name, + firtool_extra_args, + verilog_dialect, + verilog_debug, + } = *job.additional_job_data(); + args.write_interned_arg(firrtl_file_name); + args.write_arg("-o"); + args.write_interned_arg(unadjusted_verilog_file_name); + if verilog_debug { + args.write_args(["-g", "--preserve-values=all"]); + } + if let Some(dialect) = verilog_dialect { + args.write_args(dialect.firtool_extra_args().iter().copied()); + } + args.write_interned_args(firtool_extra_args); + } + + fn current_dir(job: &ExternalCommandJob) -> Option> { + Some(job.output_dir()) + } + + fn job_kind_name() -> Interned { + "unadjusted-verilog".intern() + } + + fn subcommand_hidden() -> bool { + true + } + + fn run_even_if_cached_arg_name() -> Interned { + "firtool-run-even-if-cached".intern() + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct VerilogJobKind; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Args)] +#[non_exhaustive] +pub struct VerilogJobArgs {} + +impl ToArgs for VerilogJobArgs { + fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { + let Self {} = self; + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct VerilogJob { + output_dir: Interned, + unadjusted_verilog_file: Interned, + main_verilog_file: Interned, +} + +impl VerilogJob { + pub fn output_dir(&self) -> Interned { + self.output_dir + } + pub fn unadjusted_verilog_file(&self) -> Interned { + self.unadjusted_verilog_file + } + pub fn main_verilog_file(&self) -> Interned { + self.main_verilog_file + } + #[track_caller] + pub fn unwrap_additional_files(additional_files: &JobItem) -> &[Interned] { + match additional_files { + JobItem::DynamicPaths { + paths, + source_job_name, + } if *source_job_name == VerilogJobKind.name() => paths, + v => panic!("expected VerilogJob's additional files JobItem: {v:?}"), + } + } + pub fn all_verilog_files( + main_verilog_file: Interned, + additional_files: &[Interned], + ) -> eyre::Result]>> { + let mut retval = Vec::with_capacity(additional_files.len().saturating_add(1)); + for verilog_file in [main_verilog_file].iter().chain(additional_files) { + if !["v", "sv"] + .iter() + .any(|extension| verilog_file.extension() == Some(extension.as_ref())) + { + continue; + } + let verilog_file = std::path::absolute(verilog_file).wrap_err_with(|| { + format!("converting {verilog_file:?} to an absolute path failed") + })?; + if verilog_file + .as_os_str() + .as_encoded_bytes() + .iter() + .any(|&ch| (ch != b' ' && ch != b'\t' && ch.is_ascii_whitespace()) || ch == b'"') + { + bail!("verilog file path contains characters that aren't permitted"); + } + retval.push(verilog_file.intern_deref()); + } + Ok(retval.intern_slice()) + } +} + +impl JobKind for VerilogJobKind { + type Args = VerilogJobArgs; + type Job = VerilogJob; + type Dependencies = JobKindAndDependencies>; + + fn dependencies(self) -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + args: JobArgsAndDependencies, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result> { + args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { + let VerilogJobArgs {} = args; + let base_job = dependencies.get_job::(); + Ok(VerilogJob { + output_dir: base_job.output_dir(), + unadjusted_verilog_file: dependencies + .job + .job + .additional_job_data() + .unadjusted_verilog_file(), + main_verilog_file: base_job.file_with_ext("v"), + }) + }) + } + + fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::Path { + path: job.unadjusted_verilog_file, + }] + .intern_slice() + } + + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [ + JobItemName::Path { + path: job.main_verilog_file, + }, + JobItemName::DynamicPaths { + source_job_name: self.name(), + }, + ] + .intern_slice() + } + + fn name(self) -> Interned { + "verilog".intern() + } + + fn external_command_params(self, _job: &Self::Job) -> Option { + None + } + + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + _params: &JobParams, + _global_params: &GlobalParams, + _acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); + let input = std::fs::read_to_string(job.unadjusted_verilog_file())?; + let file_separator_prefix = "\n// ----- 8< ----- FILE \""; + let file_separator_suffix = "\" ----- 8< -----\n\n"; + let mut input = &*input; + let main_verilog_file = job.main_verilog_file(); + let mut file_name = Some(main_verilog_file); + let mut additional_outputs = Vec::new(); + loop { + let (chunk, next_file_name) = if let Some((chunk, rest)) = + input.split_once(file_separator_prefix) + { + let Some((next_file_name, rest)) = rest.split_once(file_separator_suffix) else { + bail!( + "parsing firtool's output failed: found {file_separator_prefix:?} but no {file_separator_suffix:?}" + ); + }; + input = rest; + let next_file_name = job.output_dir.join(next_file_name).intern_deref(); + additional_outputs.push(next_file_name); + (chunk, Some(next_file_name)) + } else { + (mem::take(&mut input), None) + }; + let Some(file_name) = mem::replace(&mut file_name, next_file_name) else { + break; + }; + std::fs::write(&file_name, chunk)?; + } + Ok(vec![ + JobItem::Path { + path: main_verilog_file, + }, + JobItem::DynamicPaths { + paths: additional_outputs, + source_job_name: self.name(), + }, + ]) + } +} + +pub(crate) fn built_in_job_kinds() -> impl IntoIterator { + [ + DynJobKind::new(ExternalCommandJobKind::::new()), + DynJobKind::new(VerilogJobKind), + ] +} diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 55843ea5..1471f3ac 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -3,12 +3,14 @@ use crate::{ expr::{ - CastToBits, Expr, ReduceBits, ToExpr, - ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, + CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType, + Valueless, + ops::{ArrayLiteral, BundleLiteral}, + value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue}, }, int::{Bool, DynSize}, - intern::{Intern, Interned}, - sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, + intern::{Intern, InternSlice, Interned}, + sim::value::{SimValue, SimValueEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize, @@ -18,7 +20,7 @@ use crate::{ util::HashMap, }; use serde::{Deserialize, Serialize}; -use std::{fmt, marker::PhantomData}; +use std::{borrow::Cow, fmt, marker::PhantomData}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct BundleField { @@ -271,7 +273,6 @@ impl Type for Bundle { pub trait BundleType: Type { type Builder: Default; - type FilledBuilder: ToExpr; fn fields(&self) -> Interned<[BundleField]>; } @@ -318,7 +319,7 @@ impl<'a> BundleSimValueFromOpaque<'a> { #[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)); + assert_eq!(field_ty, field_value.ty()); SimValue::opaque_mut(field_value).clone_from_slice(field_opaque); } } @@ -354,7 +355,7 @@ impl<'a> BundleSimValueToOpaque<'a> { else { panic!("tried to write too many fields with BundleSimValueToOpaque"); }; - assert_eq!(T::from_canonical(ty), SimValue::ty(field_value)); + assert_eq!(T::from_canonical(ty), field_value.ty()); self.writer.fill_prefix_with(ty.size(), |writer| { writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice()) }); @@ -374,17 +375,8 @@ impl<'a> BundleSimValueToOpaque<'a> { #[derive(Default)] pub struct NoBuilder; -pub struct Unfilled(PhantomData); - -impl Default for Unfilled { - fn default() -> Self { - Self(PhantomData) - } -} - impl BundleType for Bundle { type Builder = NoBuilder; - type FilledBuilder = Expr; fn fields(&self) -> Interned<[BundleField]> { self.0.fields } @@ -420,15 +412,14 @@ macro_rules! impl_tuple_builder_fields { ) => { impl< $($head_type_var,)* - $cur_type_var: Type, $($tail_type_var,)* > TupleBuilder<( $($head_type_var,)* - Unfilled<$cur_type_var>, + (), $($tail_type_var,)* )> { - pub fn $cur_field(self, $cur_var: impl ToExpr) -> TupleBuilder<( + pub fn $cur_field<$cur_type_var: Type>(self, $cur_var: impl ToExpr) -> TupleBuilder<( $($head_type_var,)* Expr<$cur_type_var>, $($tail_type_var,)* @@ -452,6 +443,12 @@ macro_rules! impl_tuple_builder_fields { ($global:tt []) => {}; } +macro_rules! get_unit_ty { + ($($tt:tt)*) => { + () + }; +} + macro_rules! impl_tuples { ( [$({ @@ -545,11 +542,10 @@ macro_rules! impl_tuples { } } impl<$($T: Type,)*> BundleType for ($($T,)*) { - type Builder = TupleBuilder<($(Unfilled<$T>,)*)>; - type FilledBuilder = TupleBuilder<($(Expr<$T>,)*)>; + type Builder = TupleBuilder<($(get_unit_ty!($T),)*)>; fn fields(&self) -> Interned<[BundleField]> { let ($($var,)*) = self; - [$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern() + [$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*].intern_slice() } } impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) { @@ -572,25 +568,56 @@ macro_rules! impl_tuples { builder.finish() }; } - impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) { + impl<'a, $($T: ToSimValue,)*> ToSimValueInner<'a> for ($($T,)*) + where + Self: ValueType, + { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + let ($($var,)*) = this; + Cow::Owned(($($var.to_sim_value(),)*)) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + let ($($var,)*) = this; + Cow::Owned(($($var.into_sim_value(),)*)) + } + } + impl<$($T: ValueType,)*> ValueType for ($($T,)*) + where + ValueCategoryValue: ValueCategoryCommon<($($T::ValueCategory,)*)>, + { type Type = ($($T::Type,)*); - + type ValueCategory = >::Common; + fn ty(&self) -> Self::Type { + let ($($var,)*) = self; + ($($var.ty(),)*) + } + } + impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) + where + Self: ValueType, + { fn to_expr(&self) -> Expr { let ($($var,)*) = self; $(let $var = $var.to_expr();)* - let ty = ($(Expr::ty($var),)*); + let ty = ($($var.ty(),)*); let field_values = [$(Expr::canonical($var)),*]; - BundleLiteral::new(ty, field_values[..].intern()).to_expr() + BundleLiteral::new(ty, field_values.intern_slice()).to_expr() + } + } + impl<$($T: Type,)*> ValueType for TupleBuilder<($(Expr<$T>,)*)> { + type Type = ($($T,)*); + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + let ($($var,)*) = self.0; + ($($var.ty(),)*) } } impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> { - type Type = ($($T,)*); - fn to_expr(&self) -> Expr { let ($($var,)*) = self.0; - let ty = ($(Expr::ty($var),)*); + let ty = ($($var.ty(),)*); let field_values = [$(Expr::canonical($var)),*]; - BundleLiteral::new(ty, field_values[..].intern()).to_expr() + BundleLiteral::new(ty, field_values.intern_slice()).to_expr() } } impl<$($T: ToSimValueWithType,)*> ToSimValueWithType for ($($T,)*) { @@ -624,7 +651,7 @@ macro_rules! impl_tuples { }; let mut opaque = OpaqueSimValue::empty(); $(let $var = $var.into_sim_value_with_type($ty_var.ty); - assert_eq!(SimValue::ty(&$var), $ty_var.ty); + assert_eq!($var.ty(), $ty_var.ty); opaque.extend_from_slice(SimValue::opaque(&$var).as_slice()); )* SimValue::from_opaque(ty, opaque) @@ -646,53 +673,82 @@ macro_rules! impl_tuples { SimValue::from_value(ty, ($($var,)*)) } } - impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { - type Type = ($($T::Type,)*); + impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) + where + Self: ValueType, + { #[track_caller] fn to_sim_value(&self) -> SimValue { let ($($var,)*) = self; $(let $var = $var.to_sim_value();)* - SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) + SimValue::from_value(($($var.ty(),)*), ($($var,)*)) } #[track_caller] fn into_sim_value(self) -> SimValue { let ($($var,)*) = self; $(let $var = $var.to_sim_value();)* - SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) + SimValue::from_value(($($var.ty(),)*), ($($var,)*)) } } - impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { - fn cmp_eq(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + impl<$($Lhs: Type + HdlPartialEqImpl<$Rhs>, $Rhs: Type,)*> HdlPartialEqImpl<($($Rhs,)*)> for ($($Lhs,)*) { + #[track_caller] + fn cmp_value_eq( + lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + rhs: ($($Rhs,)*), + rhs_value: Cow<'_, <($($Rhs,)*) as Type>::SimValue>, + ) -> bool { + #![allow(unused_variables)] + let ($($lhs_var,)*) = &*lhs_value; + let ($($rhs_var,)*) = &*rhs_value; + let retval = true; + $(let retval = retval && $Lhs::cmp_value_eq(lhs.$num, Cow::Borrowed($lhs_var), rhs.$num, Cow::Borrowed($rhs_var));)* + retval + } + + #[track_caller] + fn cmp_expr_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)),)*]), + FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_eq($lhs_var, $rhs_var)),)*]), ) .cast_to_bits() .all_one_bits() } - fn cmp_ne(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + #[track_caller] + fn cmp_expr_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)),)*]), + FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_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 + + #[track_caller] + fn cmp_valueless_eq(lhs: Valueless, rhs: Valueless<($($Rhs,)*)>) -> Valueless { + let ($($lhs_var,)*) = lhs.ty(); + let ($($rhs_var,)*) = rhs.ty(); + // let them check that the types can be compared + $($Lhs::cmp_valueless_eq(Valueless::new($lhs_var), Valueless::new($rhs_var));)* + Valueless::new(Bool) + } + + #[track_caller] + fn cmp_valueless_ne(lhs: Valueless, rhs: Valueless<($($Rhs,)*)>) -> Valueless { + let ($($lhs_var,)*) = lhs.ty(); + let ($($rhs_var,)*) = rhs.ty(); + // let them check that the types can be compared + $($Lhs::cmp_valueless_ne(Valueless::new($lhs_var), Valueless::new($rhs_var));)* + Valueless::new(Bool) } } + impl<$($T: SimValueEq + HdlPartialEqImpl<$T>,)*> SimValueEq for ($($T,)*) {} }; ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { impl_tuples!([$($lhs)*] []); @@ -781,9 +837,16 @@ impl Default for PhantomDataBuilder { } } -impl ToExpr for PhantomDataBuilder { +impl ValueType for PhantomDataBuilder { type Type = PhantomData; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + PhantomData + } +} + +impl ToExpr for PhantomDataBuilder { fn to_expr(&self) -> Expr { PhantomData.to_expr() } @@ -791,7 +854,6 @@ impl ToExpr for PhantomDataBuilder { impl BundleType for PhantomData { type Builder = PhantomDataBuilder; - type FilledBuilder = PhantomDataBuilder; fn fields(&self) -> Interned<[BundleField]> { Interned::default() } @@ -810,17 +872,22 @@ impl StaticType for PhantomData { const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; } -impl ToExpr for PhantomData { +impl ValueType for PhantomData { type Type = PhantomData; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + PhantomData + } +} + +impl ToExpr for PhantomData { fn to_expr(&self) -> Expr { BundleLiteral::new(PhantomData, Interned::default()).to_expr() } } impl ToSimValue for PhantomData { - type Type = PhantomData; - #[track_caller] fn to_sim_value(&self) -> SimValue { SimValue::from_value(*self, *self) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs deleted file mode 100644 index 6fb4b5e7..00000000 --- a/crates/fayalite/src/cli.rs +++ /dev/null @@ -1,806 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information -use crate::{ - bundle::{Bundle, BundleType}, - firrtl::{self, ExportOptions}, - intern::Interned, - module::Module, - util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8}, -}; -use clap::{ - Parser, Subcommand, ValueEnum, ValueHint, - builder::{OsStringValueParser, TypedValueParser}, -}; -use eyre::{Report, eyre}; -use serde::{Deserialize, Serialize}; -use std::{ - error, - ffi::OsString, - fmt::{self, Write}, - fs, io, mem, - path::{Path, PathBuf}, - process, -}; -use tempfile::TempDir; - -pub type Result = std::result::Result; - -pub struct CliError(Report); - -impl fmt::Debug for CliError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::Display for CliError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl error::Error for CliError {} - -impl From for CliError { - fn from(value: io::Error) -> Self { - CliError(Report::new(value)) - } -} - -pub trait RunPhase { - type Output; - fn run(&self, arg: Arg) -> Result { - self.run_with_job(arg, &mut AcquiredJob::acquire()) - } - fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result; -} - -#[derive(Parser, Debug, Clone)] -#[non_exhaustive] -pub struct BaseArgs { - /// the directory to put the generated main output file and associated files in - #[arg(short, long, value_hint = ValueHint::DirPath, required = true)] - pub output: Option, - /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo - #[arg(long)] - pub file_stem: Option, - #[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")] - pub keep_temp_dir: bool, - #[arg(skip = false)] - pub redirect_output_for_rust_test: bool, -} - -impl BaseArgs { - fn make_firrtl_file_backend(&self) -> Result<(firrtl::FileBackend, Option)> { - let (dir_path, temp_dir) = match &self.output { - Some(output) => (output.clone(), None), - None => { - let temp_dir = TempDir::new()?; - if self.keep_temp_dir { - let temp_dir = temp_dir.into_path(); - println!("created temporary directory: {}", temp_dir.display()); - (temp_dir, None) - } else { - (temp_dir.path().to_path_buf(), Some(temp_dir)) - } - } - }; - Ok(( - firrtl::FileBackend { - dir_path, - top_fir_file_stem: self.file_stem.clone(), - circuit_name: None, - }, - temp_dir, - )) - } - /// handles possibly redirecting the command's output for Rust tests - pub fn run_external_command( - &self, - _acquired_job: &mut AcquiredJob, - mut command: process::Command, - mut captured_output: Option<&mut String>, - ) -> io::Result { - if self.redirect_output_for_rust_test || captured_output.is_some() { - let (reader, writer) = os_pipe::pipe()?; - let mut reader = io::BufReader::new(reader); - command.stderr(writer.try_clone()?); - command.stdout(writer); // must not leave writer around after spawning child - command.stdin(process::Stdio::null()); - let mut child = command.spawn()?; - drop(command); // close writers - Ok(loop { - let status = child.try_wait()?; - streaming_read_utf8(&mut reader, |s| { - if let Some(captured_output) = captured_output.as_deref_mut() { - captured_output.push_str(s); - } - // use print! so output goes to Rust test output capture - print!("{s}"); - io::Result::Ok(()) - })?; - if let Some(status) = status { - break status; - } - }) - } else { - command.status() - } - } -} - -#[derive(Parser, Debug, Clone)] -#[non_exhaustive] -pub struct FirrtlArgs { - #[command(flatten)] - pub base: BaseArgs, - #[command(flatten)] - pub export_options: ExportOptions, -} - -#[derive(Debug)] -#[non_exhaustive] -pub struct FirrtlOutput { - pub file_stem: String, - pub top_module: String, - pub output_dir: PathBuf, - pub temp_dir: Option, -} - -impl FirrtlOutput { - pub fn file_with_ext(&self, ext: &str) -> PathBuf { - let mut retval = self.output_dir.join(&self.file_stem); - retval.set_extension(ext); - retval - } - pub fn firrtl_file(&self) -> PathBuf { - self.file_with_ext("fir") - } -} - -impl FirrtlArgs { - fn run_impl( - &self, - top_module: Module, - _acquired_job: &mut AcquiredJob, - ) -> Result { - let (file_backend, temp_dir) = self.base.make_firrtl_file_backend()?; - let firrtl::FileBackend { - top_fir_file_stem, - circuit_name, - dir_path, - } = firrtl::export(file_backend, &top_module, self.export_options)?; - Ok(FirrtlOutput { - file_stem: top_fir_file_stem.expect( - "export is known to set the file stem from the circuit name if not provided", - ), - top_module: circuit_name.expect("export is known to set the circuit name"), - output_dir: dir_path, - temp_dir, - }) - } -} - -impl RunPhase> for FirrtlArgs { - type Output = FirrtlOutput; - fn run_with_job( - &self, - top_module: Module, - acquired_job: &mut AcquiredJob, - ) -> Result { - self.run_impl(top_module.canonical(), acquired_job) - } -} - -impl RunPhase>> for FirrtlArgs { - type Output = FirrtlOutput; - fn run_with_job( - &self, - top_module: Interned>, - acquired_job: &mut AcquiredJob, - ) -> Result { - self.run_with_job(*top_module, acquired_job) - } -} - -/// based on [LLVM Circt's recommended lowering options -/// ](https://circt.llvm.org/docs/VerilogGeneration/#recommended-loweringoptions-by-target) -#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub enum VerilogDialect { - Questa, - Spyglass, - Verilator, - Vivado, - Yosys, -} - -impl fmt::Display for VerilogDialect { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -impl VerilogDialect { - pub fn as_str(self) -> &'static str { - match self { - VerilogDialect::Questa => "questa", - VerilogDialect::Spyglass => "spyglass", - VerilogDialect::Verilator => "verilator", - VerilogDialect::Vivado => "vivado", - VerilogDialect::Yosys => "yosys", - } - } - pub fn firtool_extra_args(self) -> &'static [&'static str] { - match self { - VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"], - VerilogDialect::Spyglass => { - &["--lowering-options=explicitBitcast,disallowExpressionInliningInPorts"] - } - VerilogDialect::Verilator => &[ - "--lowering-options=locationInfoStyle=wrapInAtSquareBracket,disallowLocalVariables", - ], - VerilogDialect::Vivado => &["--lowering-options=mitigateVivadoArrayIndexConstPropBug"], - VerilogDialect::Yosys => { - &["--lowering-options=disallowLocalVariables,disallowPackedArrays"] - } - } - } -} - -#[derive(Parser, Debug, Clone)] -#[non_exhaustive] -pub struct VerilogArgs { - #[command(flatten)] - pub firrtl: FirrtlArgs, - #[arg( - long, - default_value = "firtool", - env = "FIRTOOL", - value_hint = ValueHint::CommandName, - value_parser = OsStringValueParser::new().try_map(which) - )] - pub firtool: PathBuf, - #[arg(long)] - pub firtool_extra_args: Vec, - /// adapt the generated Verilog for a particular toolchain - #[arg(long)] - pub verilog_dialect: Option, - #[arg(long, short = 'g')] - pub debug: bool, -} - -#[derive(Debug)] -#[non_exhaustive] -pub struct VerilogOutput { - pub firrtl: FirrtlOutput, - pub verilog_files: Vec, - pub contents_hash: Option, -} - -impl VerilogOutput { - pub fn main_verilog_file(&self) -> PathBuf { - self.firrtl.file_with_ext("v") - } - fn unadjusted_verilog_file(&self) -> PathBuf { - self.firrtl.file_with_ext("unadjusted.v") - } -} - -impl VerilogArgs { - fn process_unadjusted_verilog_file(&self, mut output: VerilogOutput) -> Result { - let input = fs::read_to_string(output.unadjusted_verilog_file())?; - let file_separator_prefix = "\n// ----- 8< ----- FILE \""; - let file_separator_suffix = "\" ----- 8< -----\n\n"; - let mut input = &*input; - output.contents_hash = Some(blake3::hash(input.as_bytes())); - let main_verilog_file = output.main_verilog_file(); - let mut file_name: Option<&Path> = Some(&main_verilog_file); - loop { - let (chunk, next_file_name) = if let Some((chunk, rest)) = - 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:?}" - ))); - }; - input = rest; - (chunk, Some(next_file_name.as_ref())) - } else { - (mem::take(&mut input), None) - }; - let Some(file_name) = mem::replace(&mut file_name, next_file_name) else { - break; - }; - let file_name = output.firrtl.output_dir.join(file_name); - fs::write(&file_name, chunk)?; - if let Some(extension) = file_name.extension() { - if extension == "v" || extension == "sv" { - output.verilog_files.push(file_name); - } - } - } - Ok(output) - } - fn run_impl( - &self, - firrtl_output: FirrtlOutput, - acquired_job: &mut AcquiredJob, - ) -> Result { - let Self { - firrtl, - firtool, - firtool_extra_args, - verilog_dialect, - debug, - } = self; - let output = VerilogOutput { - firrtl: firrtl_output, - verilog_files: vec![], - contents_hash: None, - }; - let mut cmd = process::Command::new(firtool); - cmd.arg(output.firrtl.firrtl_file()); - cmd.arg("-o"); - cmd.arg(output.unadjusted_verilog_file()); - if *debug { - cmd.arg("-g"); - cmd.arg("--preserve-values=all"); - } - if let Some(dialect) = verilog_dialect { - cmd.args(dialect.firtool_extra_args()); - } - cmd.args(firtool_extra_args); - cmd.current_dir(&output.firrtl.output_dir); - let status = firrtl.base.run_external_command(acquired_job, cmd, None)?; - if status.success() { - self.process_unadjusted_verilog_file(output) - } else { - Err(CliError(eyre!( - "running {} failed: {status}", - self.firtool.display() - ))) - } - } -} - -impl RunPhase for VerilogArgs -where - FirrtlArgs: RunPhase, -{ - type Output = VerilogOutput; - fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result { - let firrtl_output = self.firrtl.run_with_job(arg, acquired_job)?; - self.run_impl(firrtl_output, acquired_job) - } -} - -#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default)] -#[non_exhaustive] -pub enum FormalMode { - #[default] - BMC, - Prove, - Live, - Cover, -} - -impl FormalMode { - pub fn as_str(self) -> &'static str { - match self { - FormalMode::BMC => "bmc", - FormalMode::Prove => "prove", - FormalMode::Live => "live", - FormalMode::Cover => "cover", - } - } -} - -impl fmt::Display for FormalMode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -#[derive(Clone)] -struct FormalAdjustArgs; - -impl clap::FromArgMatches for FormalAdjustArgs { - fn from_arg_matches(_matches: &clap::ArgMatches) -> Result { - Ok(Self) - } - - fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> { - Ok(()) - } -} - -impl clap::Args for FormalAdjustArgs { - fn augment_args(cmd: clap::Command) -> clap::Command { - cmd.mut_arg("output", |arg| arg.required(false)) - .mut_arg("verilog_dialect", |arg| { - arg.default_value(VerilogDialect::Yosys.to_string()) - .hide(true) - }) - } - - fn augment_args_for_update(cmd: clap::Command) -> clap::Command { - Self::augment_args(cmd) - } -} - -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 { - #[command(flatten)] - pub verilog: VerilogArgs, - #[arg( - long, - default_value = "sby", - env = "SBY", - value_hint = ValueHint::CommandName, - value_parser = OsStringValueParser::new().try_map(which) - )] - pub sby: PathBuf, - #[arg(long)] - pub sby_extra_args: Vec, - #[arg(long, default_value_t)] - pub mode: FormalMode, - #[arg(long, default_value_t = Self::DEFAULT_DEPTH)] - pub depth: u64, - #[arg(long, default_value = "z3")] - pub solver: String, - #[arg(long)] - pub smtbmc_extra_args: Vec, - #[arg(long, default_value_t = true, env = "FAYALITE_CACHE_RESULTS")] - pub cache_results: bool, - #[command(flatten)] - _formal_adjust_args: FormalAdjustArgs, -} - -impl fmt::Debug for FormalArgs { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - verilog, - sby, - sby_extra_args, - mode, - depth, - solver, - smtbmc_extra_args, - cache_results, - _formal_adjust_args: _, - } = self; - f.debug_struct("FormalArgs") - .field("verilog", verilog) - .field("sby", sby) - .field("sby_extra_args", sby_extra_args) - .field("mode", mode) - .field("depth", depth) - .field("solver", solver) - .field("smtbmc_extra_args", smtbmc_extra_args) - .field("cache_results", cache_results) - .finish_non_exhaustive() - } -} - -impl FormalArgs { - pub const DEFAULT_DEPTH: u64 = 20; -} - -#[derive(Debug)] -#[non_exhaustive] -pub struct FormalOutput { - pub verilog: VerilogOutput, -} - -impl FormalOutput { - pub fn sby_file(&self) -> PathBuf { - self.verilog.firrtl.file_with_ext("sby") - } - pub fn cache_file(&self) -> PathBuf { - self.verilog.firrtl.file_with_ext("cache.json") - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[non_exhaustive] -pub struct FormalCacheOutput {} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[non_exhaustive] -pub enum FormalCacheVersion { - V1, -} - -impl FormalCacheVersion { - pub const CURRENT: Self = Self::V1; -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[non_exhaustive] -pub struct FormalCache { - pub version: FormalCacheVersion, - pub contents_hash: blake3::Hash, - pub stdout_stderr: String, - pub result: Result, -} - -impl FormalCache { - pub fn new( - version: FormalCacheVersion, - contents_hash: blake3::Hash, - stdout_stderr: String, - result: Result, - ) -> Self { - Self { - version, - contents_hash, - stdout_stderr, - result, - } - } -} - -impl FormalArgs { - fn sby_contents(&self, output: &FormalOutput) -> Result { - let Self { - verilog: _, - sby: _, - sby_extra_args: _, - mode, - depth, - smtbmc_extra_args, - solver, - cache_results: _, - _formal_adjust_args: _, - } = self; - let smtbmc_options = smtbmc_extra_args.join(" "); - let top_module = &output.verilog.firrtl.top_module; - let mut retval = format!( - "[options]\n\ - mode {mode}\n\ - depth {depth}\n\ - wait on\n\ - \n\ - [engines]\n\ - smtbmc {solver} -- -- {smtbmc_options}\n\ - \n\ - [script]\n" - ); - for verilog_file in &output.verilog.verilog_files { - let verilog_file = verilog_file - .to_str() - .ok_or_else(|| CliError(eyre!("verilog file path is not UTF-8")))?; - if verilog_file.contains(|ch: char| { - (ch != ' ' && ch != '\t' && ch.is_ascii_whitespace()) || ch == '"' - }) { - return Err(CliError(eyre!( - "verilog file path contains characters that aren't permitted" - ))); - } - writeln!(retval, "read_verilog -sv -formal \"{verilog_file}\"").unwrap(); - } - // workaround for wires disappearing -- set `keep` on all wires - writeln!(retval, "hierarchy -top {top_module}").unwrap(); - writeln!(retval, "proc").unwrap(); - writeln!(retval, "setattr -set keep 1 w:\\*").unwrap(); - writeln!(retval, "prep").unwrap(); - Ok(retval) - } - fn run_impl( - &self, - verilog_output: VerilogOutput, - acquired_job: &mut AcquiredJob, - ) -> Result { - let output = FormalOutput { - verilog: verilog_output, - }; - let sby_file = output.sby_file(); - let sby_contents = self.sby_contents(&output)?; - let contents_hash = output.verilog.contents_hash.map(|verilog_hash| { - let mut hasher = blake3::Hasher::new(); - hasher.update(verilog_hash.as_bytes()); - hasher.update(sby_contents.as_bytes()); - hasher.update(&(self.sby_extra_args.len() as u64).to_le_bytes()); - for sby_extra_arg in self.sby_extra_args.iter() { - hasher.update(&(sby_extra_arg.len() as u64).to_le_bytes()); - hasher.update(sby_extra_arg.as_bytes()); - } - hasher.finalize() - }); - std::fs::write(&sby_file, sby_contents)?; - let mut cmd = process::Command::new(&self.sby); - cmd.arg("-j1"); // sby seems not to respect job count in parallel mode - cmd.arg("-f"); - cmd.arg(sby_file.file_name().unwrap()); - cmd.args(&self.sby_extra_args); - cmd.current_dir(&output.verilog.firrtl.output_dir); - let mut captured_output = String::new(); - let cache_file = output.cache_file(); - let do_cache = if let Some(contents_hash) = contents_hash.filter(|_| self.cache_results) { - if let Some(FormalCache { - version: FormalCacheVersion::CURRENT, - contents_hash: cache_contents_hash, - stdout_stderr, - result, - }) = fs::read(&cache_file) - .ok() - .and_then(|v| serde_json::from_slice(&v).ok()) - { - if cache_contents_hash == contents_hash { - println!("Using cached formal result:\n{stdout_stderr}"); - return match result { - Ok(FormalCacheOutput {}) => Ok(output), - Err(error) => Err(CliError(eyre::Report::msg(error))), - }; - } - } - true - } else { - false - }; - let _ = fs::remove_file(&cache_file); - let status = self.verilog.firrtl.base.run_external_command( - acquired_job, - cmd, - do_cache.then_some(&mut captured_output), - )?; - let result = if status.success() { - Ok(output) - } else { - Err(CliError(eyre!( - "running {} failed: {status}", - self.sby.display() - ))) - }; - fs::write( - cache_file, - serde_json::to_string_pretty(&FormalCache { - version: FormalCacheVersion::CURRENT, - contents_hash: contents_hash.unwrap(), - stdout_stderr: captured_output, - result: match &result { - Ok(FormalOutput { verilog: _ }) => Ok(FormalCacheOutput {}), - Err(error) => Err(error.to_string()), - }, - }) - .expect("serialization shouldn't ever fail"), - )?; - result - } -} - -impl RunPhase for FormalArgs -where - VerilogArgs: RunPhase, -{ - type Output = FormalOutput; - fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result { - let verilog_output = self.verilog.run_with_job(arg, acquired_job)?; - self.run_impl(verilog_output, acquired_job) - } -} - -#[derive(Subcommand, Debug)] -enum CliCommand { - /// Generate FIRRTL - Firrtl(FirrtlArgs), - /// Generate Verilog - Verilog(VerilogArgs), - /// Run a formal proof - Formal(FormalArgs), -} - -/// a simple CLI -/// -/// Use like: -/// -/// ```no_run -/// # use fayalite::prelude::*; -/// # #[hdl_module] -/// # fn my_module() {} -/// use fayalite::cli; -/// -/// fn main() -> cli::Result { -/// cli::Cli::parse().run(my_module()) -/// } -/// ``` -/// -/// You can also use it with a larger [`clap`]-based CLI like so: -/// -/// ```no_run -/// # use fayalite::prelude::*; -/// # #[hdl_module] -/// # fn my_module() {} -/// use clap::{Subcommand, Parser}; -/// use fayalite::cli; -/// -/// #[derive(Subcommand)] -/// pub enum Cmd { -/// #[command(flatten)] -/// Fayalite(cli::Cli), -/// MySpecialCommand { -/// #[arg(long)] -/// foo: bool, -/// }, -/// } -/// -/// #[derive(Parser)] -/// pub struct Cli { -/// #[command(subcommand)] -/// cmd: Cmd, // or just use cli::Cli directly if you don't need more subcommands -/// } -/// -/// fn main() -> cli::Result { -/// match Cli::parse().cmd { -/// Cmd::Fayalite(v) => v.run(my_module())?, -/// Cmd::MySpecialCommand { foo } => println!("special: foo={foo}"), -/// } -/// Ok(()) -/// } -/// ``` -#[derive(Parser, Debug)] -// clear things that would be crate-specific -#[command(name = "Fayalite Simple CLI", about = None, long_about = None)] -pub struct Cli { - #[command(subcommand)] - subcommand: CliCommand, -} - -impl clap::Subcommand for Cli { - fn augment_subcommands(cmd: clap::Command) -> clap::Command { - CliCommand::augment_subcommands(cmd) - } - - fn augment_subcommands_for_update(cmd: clap::Command) -> clap::Command { - CliCommand::augment_subcommands_for_update(cmd) - } - - fn has_subcommand(name: &str) -> bool { - CliCommand::has_subcommand(name) - } -} - -impl RunPhase for Cli -where - FirrtlArgs: RunPhase, -{ - type Output = (); - fn run_with_job(&self, arg: T, acquired_job: &mut AcquiredJob) -> Result { - match &self.subcommand { - CliCommand::Firrtl(c) => { - c.run_with_job(arg, acquired_job)?; - } - CliCommand::Verilog(c) => { - c.run_with_job(arg, acquired_job)?; - } - CliCommand::Formal(c) => { - c.run_with_job(arg, acquired_job)?; - } - } - Ok(()) - } -} - -impl Cli { - /// forwards to [`clap::Parser::parse()`] so you don't have to import [`clap::Parser`] - pub fn parse() -> Self { - clap::Parser::parse() - } - /// forwards to [`RunPhase::run()`] so you don't have to import [`RunPhase`] - pub fn run(&self, top_module: T) -> Result<()> - where - Self: RunPhase, - { - RunPhase::run(self, top_module) - } -} diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 909edbdf..168142b7 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -1,10 +1,11 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - expr::{Expr, ToExpr}, + expr::{Expr, ValueType}, hdl, int::Bool, reset::{Reset, ResetType}, + sim::value::SimValue, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, @@ -91,29 +92,34 @@ impl StaticType for Clock { } pub trait ToClock { - fn to_clock(&self) -> Expr; + type Output: ValueType; + fn to_clock(&self) -> Self::Output; } impl ToClock for &'_ T { - fn to_clock(&self) -> Expr { + type Output = T::Output; + fn to_clock(&self) -> Self::Output { (**self).to_clock() } } impl ToClock for &'_ mut T { - fn to_clock(&self) -> Expr { + type Output = T::Output; + fn to_clock(&self) -> Self::Output { (**self).to_clock() } } impl ToClock for Box { - fn to_clock(&self) -> Expr { + type Output = T::Output; + fn to_clock(&self) -> Self::Output { (**self).to_clock() } } impl ToClock for Expr { - fn to_clock(&self) -> Expr { + type Output = Expr; + fn to_clock(&self) -> Self::Output { *self } } @@ -125,7 +131,25 @@ pub struct ClockDomain { } impl ToClock for bool { - fn to_clock(&self) -> Expr { - self.to_expr().to_clock() + type Output = SimValue; + + fn to_clock(&self) -> Self::Output { + SimValue::from_value(Clock, *self) + } +} + +impl ToClock for SimValue { + type Output = SimValue; + + fn to_clock(&self) -> Self::Output { + SimValue::from_value(Clock, **self) + } +} + +impl ToClock for SimValue { + type Output = SimValue; + + fn to_clock(&self) -> Self::Output { + self.clone() } } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 083072b5..a04f67a4 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -2,10 +2,7 @@ // See Notices.txt for copyright information use crate::{ - expr::{ - Expr, ToExpr, - ops::{ExprPartialEq, VariantAccess}, - }, + expr::{Expr, HdlPartialEq, HdlPartialEqImpl, ToExpr, ValueType, ops::VariantAccess}, hdl, int::{Bool, UIntValue}, intern::{Intern, Interned}, @@ -13,7 +10,7 @@ use crate::{ EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect, enum_match_variants_helper, incomplete_wire, wire, }, - sim::value::{SimValue, SimValuePartialEq}, + sim::value::SimValue, source_location::SourceLocation, ty::{ CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize, @@ -24,7 +21,7 @@ use crate::{ }; use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; use serde::{Deserialize, Serialize}; -use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; +use std::{borrow::Cow, convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub struct EnumVariant { @@ -599,7 +596,7 @@ impl<'a> EnumSimValueFromOpaque<'a> { 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)); + assert_eq!(value.ty(), T::from_canonical(variant_ty)); SimValue::bits_mut(value) .bits_mut() .copy_from_bitslice(variant_bits); @@ -711,7 +708,7 @@ impl<'a> EnumSimValueToOpaque<'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)); + assert_eq!(value.ty(), T::from_canonical(variant_ty)); self.known_variant(discriminant, Some(SimValue::opaque(value)), padding) } } @@ -732,9 +729,38 @@ pub enum HdlOption { HdlSome(T), } -impl, Rhs: Type> ExprPartialEq> for HdlOption { +impl, Rhs: Type> HdlPartialEqImpl> + for HdlOption +{ + fn cmp_value_eq( + lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + rhs: HdlOption, + rhs_value: Cow<'_, as Type>::SimValue>, + ) -> bool { + type SimValueMatch = ::SimValue; + match (&*lhs_value, &*rhs_value) { + (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlNone(_)) => { + true + } + (SimValueMatch::::HdlSome(..), SimValueMatch::>::HdlNone(_)) + | (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlSome(..)) => { + false + } + ( + SimValueMatch::::HdlSome(l, _), + SimValueMatch::>::HdlSome(r, _), + ) => HdlPartialEqImpl::cmp_value_eq( + lhs.HdlSome, + Cow::Borrowed(&**l), + rhs.HdlSome, + Cow::Borrowed(&**r), + ), + } + } + #[hdl] - fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_eq(lhs: Expr, rhs: Expr>) -> Expr { #[hdl] let cmp_eq = wire(); #[hdl] @@ -743,7 +769,7 @@ impl, Rhs: Type> ExprPartialEq> fo { #[hdl] match rhs { - HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)), + HdlSome(rhs) => connect(cmp_eq, lhs.cmp_eq(rhs)), HdlNone => connect(cmp_eq, false), } } @@ -760,7 +786,7 @@ impl, Rhs: Type> ExprPartialEq> fo } #[hdl] - fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_ne(lhs: Expr, rhs: Expr>) -> Expr { #[hdl] let cmp_ne = wire(); #[hdl] @@ -769,7 +795,7 @@ impl, Rhs: Type> ExprPartialEq> fo { #[hdl] match rhs { - HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)), + HdlSome(rhs) => connect(cmp_ne, lhs.cmp_ne(rhs)), HdlNone => connect(cmp_ne, true), } } @@ -786,25 +812,6 @@ impl, Rhs: Type> ExprPartialEq> fo } } -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() @@ -813,7 +820,7 @@ pub fn HdlNone() -> Expr> { #[allow(non_snake_case)] pub fn HdlSome(value: impl ToExpr) -> Expr> { let value = value.to_expr(); - HdlOption[Expr::ty(value)].HdlSome(value) + HdlOption[value.ty()].HdlSome(value) } impl HdlOption { @@ -853,7 +860,7 @@ impl HdlOption { let value = f(value).inspect_err(|_| { and_then_out.complete(()); // avoid error })?; - let and_then_out = and_then_out.complete(Expr::ty(value)); + let and_then_out = and_then_out.complete(value.ty()); connect(and_then_out, value); drop(some_scope); let (Wrap::<::MatchVariant>::HdlNone, none_scope) = @@ -861,7 +868,7 @@ impl HdlOption { else { unreachable!(); }; - connect(and_then_out, Expr::ty(and_then_out).HdlNone()); + connect(and_then_out, and_then_out.ty().HdlNone()); drop(none_scope); Ok(and_then_out) } @@ -879,8 +886,8 @@ impl HdlOption { #[track_caller] pub fn and(expr: Expr, opt_b: Expr>) -> Expr> { #[hdl] - let and_out = wire(Expr::ty(opt_b)); - connect(and_out, Expr::ty(opt_b).HdlNone()); + let and_out = wire(opt_b.ty()); + connect(and_out, opt_b.ty().HdlNone()); #[hdl] if let HdlSome(_) = expr { connect(and_out, opt_b); @@ -894,8 +901,8 @@ impl HdlOption { f: impl FnOnce(Expr) -> Result, E>, ) -> Result, E> { #[hdl] - let filtered = wire(Expr::ty(expr)); - connect(filtered, Expr::ty(expr).HdlNone()); + let filtered = wire(expr.ty()); + connect(filtered, expr.ty().HdlNone()); let mut f = Some(f); #[hdl] if let HdlSome(v) = expr { @@ -969,7 +976,7 @@ impl HdlOption { f: impl FnOnce(Expr) -> Expr, ) -> Expr { #[hdl] - let mapped = wire(Expr::ty(default)); + let mapped = wire(default.ty()); let mut f = Some(f); #[hdl] match expr { @@ -994,12 +1001,12 @@ impl HdlOption { match expr { HdlSome(v) => { let v = f.take().unwrap()(v); - let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v))); + let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty())); connect(mapped, v); } HdlNone => { let v = default.take().unwrap()(); - let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v))); + let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty())); connect(mapped, v); } } @@ -1009,7 +1016,7 @@ impl HdlOption { #[track_caller] pub fn or(expr: Expr, opt_b: Expr) -> Expr { #[hdl] - let or_out = wire(Expr::ty(expr)); + let or_out = wire(expr.ty()); connect(or_out, opt_b); #[hdl] if let HdlSome(_) = expr { @@ -1021,7 +1028,7 @@ impl HdlOption { #[track_caller] pub fn or_else(expr: Expr, f: impl FnOnce() -> Expr) -> Expr { #[hdl] - let or_else_out = wire(Expr::ty(expr)); + let or_else_out = wire(expr.ty()); connect(or_else_out, f()); #[hdl] if let HdlSome(_) = expr { @@ -1033,7 +1040,7 @@ impl HdlOption { #[track_caller] pub fn unwrap_or(expr: Expr, default: Expr) -> Expr { #[hdl] - let unwrap_or_else_out = wire(Expr::ty(default)); + let unwrap_or_else_out = wire(default.ty()); connect(unwrap_or_else_out, default); #[hdl] if let HdlSome(v) = expr { @@ -1045,7 +1052,7 @@ impl HdlOption { #[track_caller] pub fn unwrap_or_else(expr: Expr, f: impl FnOnce() -> Expr) -> Expr { #[hdl] - let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome); + let unwrap_or_else_out = wire(expr.ty().HdlSome); connect(unwrap_or_else_out, f()); #[hdl] if let HdlSome(v) = expr { @@ -1057,14 +1064,14 @@ impl HdlOption { #[track_caller] pub fn xor(expr: Expr, opt_b: Expr) -> Expr { #[hdl] - let xor_out = wire(Expr::ty(expr)); + let xor_out = wire(expr.ty()); #[hdl] if let HdlSome(_) = expr { #[hdl] if let HdlNone = opt_b { connect(xor_out, expr); } else { - connect(xor_out, Expr::ty(expr).HdlNone()); + connect(xor_out, expr.ty().HdlNone()); } } else { connect(xor_out, opt_b); @@ -1075,8 +1082,8 @@ impl HdlOption { #[track_caller] pub fn zip(expr: Expr, other: Expr>) -> Expr> { #[hdl] - let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]); - connect(zip_out, Expr::ty(zip_out).HdlNone()); + let zip_out = wire(HdlOption[(expr.ty().HdlSome, other.ty().HdlSome)]); + connect(zip_out, zip_out.ty().HdlNone()); #[hdl] if let HdlSome(l) = expr { #[hdl] @@ -1093,11 +1100,11 @@ impl HdlOption> { #[track_caller] pub fn flatten(expr: Expr) -> Expr> { #[hdl] - let flattened = wire(Expr::ty(expr).HdlSome); + let flattened = wire(expr.ty().HdlSome); #[hdl] match expr { HdlSome(v) => connect(flattened, v), - HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()), + HdlNone => connect(flattened, expr.ty().HdlSome.HdlNone()), } flattened } @@ -1107,7 +1114,7 @@ impl HdlOption<(T, U)> { #[hdl] #[track_caller] pub fn unzip(expr: Expr) -> Expr<(HdlOption, HdlOption)> { - let (t, u) = Expr::ty(expr).HdlSome; + let (t, u) = expr.ty().HdlSome; #[hdl] let unzipped = wire((HdlOption[t], HdlOption[u])); connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone())); diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 89e60cda..00a0cee5 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -5,11 +5,8 @@ use crate::{ array::{Array, ArrayType}, bundle::{Bundle, BundleType}, enum_::{Enum, EnumType}, - expr::{ - ops::ExprCastTo, - target::{GetTarget, Target}, - }, - int::{Bool, DynSize, IntType, SIntType, SIntValue, Size, SizeType, UInt, UIntType, UIntValue}, + expr::target::{GetTarget, Target}, + int::{Bool, DynSize, IntType, SIntValue, Size, SizeType, UInt, UIntType, UIntValue}, intern::{Intern, Interned}, memory::{DynPortType, MemPort, PortType}, module::{ @@ -19,14 +16,17 @@ use crate::{ phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, - ty::{CanonicalType, StaticType, Type, TypeWithDeref}, + sim::value::{SimValue, ToSimValue, ToSimValueWithType}, + ty::{CanonicalType, OpaqueSimValue, StaticType, Type, TypeWithDeref}, + util::{ConstBool, ConstUsize}, wire::Wire, }; use bitvec::slice::BitSlice; -use std::{convert::Infallible, fmt, ops::Deref}; +use std::{borrow::Cow, convert::Infallible, fmt, ops::Deref}; pub mod ops; pub mod target; +pub mod value_category; macro_rules! expr_enum { ( @@ -77,9 +77,18 @@ macro_rules! expr_enum { } } - impl ToExpr for $ExprEnum { + impl ValueType for $ExprEnum { type Type = CanonicalType; + type ValueCategory = value_category::ValueCategoryExpr; + fn ty(&self) -> Self::Type { + match self { + $(Self::$Variant(v) => v.ty().canonical(),)* + } + } + } + + impl ToExpr for $ExprEnum { fn to_expr(&self) -> Expr { match self { $(Self::$Variant(v) => Expr::canonical(v.to_expr()),)* @@ -282,7 +291,7 @@ impl fmt::Debug for Expr { __flow, } = self; let expr_ty = __ty.canonical(); - let enum_ty = __enum.to_expr().__ty; + 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}}" @@ -296,23 +305,20 @@ impl Expr { pub fn expr_enum(this: Self) -> Interned { this.__enum } - pub fn ty(this: Self) -> T { - this.__ty - } pub fn flow(this: Self) -> Flow { this.__flow } pub fn canonical(this: Self) -> Expr { Expr { __enum: this.__enum, - __ty: this.__ty.canonical(), + __ty: this.ty().canonical(), __flow: this.__flow, } } pub fn from_canonical(this: Expr) -> Self { Expr { __enum: this.__enum, - __ty: T::from_canonical(this.__ty), + __ty: T::from_canonical(this.ty()), __flow: this.__flow, } } @@ -322,7 +328,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: T::from_dyn_int(this.__ty), + __ty: T::from_dyn_int(this.ty()), __flow: this.__flow, } } @@ -332,7 +338,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: this.__ty.as_dyn_int(), + __ty: this.ty().as_dyn_int(), __flow: this.__flow, } } @@ -342,7 +348,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: Bundle::new(this.__ty.fields()), + __ty: Bundle::new(this.ty().fields()), __flow: this.__flow, } } @@ -352,7 +358,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: T::from_canonical(CanonicalType::Bundle(this.__ty)), + __ty: T::from_canonical(CanonicalType::Bundle(this.ty())), __flow: this.__flow, } } @@ -369,7 +375,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: Enum::new(this.__ty.variants()), + __ty: Enum::new(this.ty().variants()), __flow: this.__flow, } } @@ -379,7 +385,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: T::from_canonical(CanonicalType::Enum(this.__ty)), + __ty: T::from_canonical(CanonicalType::Enum(this.ty())), __flow: this.__flow, } } @@ -409,7 +415,7 @@ impl Expr> { pub fn as_dyn_array(this: Self) -> Expr { Expr { __enum: this.__enum, - __ty: this.__ty.as_dyn_array(), + __ty: this.ty().as_dyn_array(), __flow: this.__flow, } } @@ -435,54 +441,41 @@ impl Visit for Expr { } } -pub trait ToExpr { - type Type: Type; +pub trait ToExpr: ValueType + ToValueless { fn to_expr(&self) -> Expr; } impl ToExpr for Expr { - type Type = T; - fn to_expr(&self) -> Expr { *self } } impl ToExpr for &'_ T { - type Type = T::Type; - fn to_expr(&self) -> Expr { T::to_expr(self) } } impl ToExpr for &'_ mut T { - type Type = T::Type; - fn to_expr(&self) -> Expr { T::to_expr(self) } } impl ToExpr for Box { - type Type = T::Type; - fn to_expr(&self) -> Expr { T::to_expr(self) } } impl ToExpr for Interned { - type Type = T::Type; - fn to_expr(&self) -> Expr { T::to_expr(self) } } impl ToExpr for UIntValue { - type Type = UIntType; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::UIntLiteral(self.clone().as_dyn_int().intern()).intern(), @@ -493,8 +486,6 @@ impl ToExpr for UIntValue { } impl ToExpr for SIntValue { - type Type = SIntType; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::SIntLiteral(self.clone().as_dyn_int().intern()).intern(), @@ -504,9 +495,16 @@ impl ToExpr for SIntValue { } } -impl ToExpr for bool { +impl ValueType for bool { type Type = Bool; + type ValueCategory = value_category::ValueCategoryValue; + fn ty(&self) -> Self::Type { + Bool + } +} + +impl ToExpr for bool { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::BoolLiteral(*self).intern(), @@ -537,8 +535,6 @@ impl Flow { } impl ToExpr for ModuleIO { - type Type = T; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::ModuleIO(self.canonical()).intern_sized(), @@ -561,8 +557,6 @@ impl GetTarget for ModuleIO { } impl ToExpr for Instance { - type Type = T; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::Instance(self.canonical()).intern_sized(), @@ -585,8 +579,6 @@ impl GetTarget for Instance { } impl ToExpr for Wire { - type Type = T; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::Wire(self.canonical()).intern_sized(), @@ -609,8 +601,6 @@ impl GetTarget for Wire { } impl ToExpr for Reg { - type Type = T; - fn to_expr(&self) -> Expr { struct Dispatch; impl ResetTypeDispatch for Dispatch { @@ -650,8 +640,6 @@ impl GetTarget for Reg { } impl ToExpr for MemPort { - type Type = T::Port; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::MemPort(self.canonical()).intern_sized(), @@ -673,69 +661,840 @@ impl GetTarget for MemPort { } } -pub trait HdlPartialEq { - fn cmp_eq(self, rhs: Rhs) -> Expr; - fn cmp_ne(self, rhs: Rhs) -> Expr; +macro_rules! impl_hdl_cmp { + ( + #[ + impl_helper = $HdlCmpImplHelper:ident, + $(impl_helper_base = $HdlCmpImplHelperBase:ident,)? + impl_helper_sealed = $HdlCmpImplHelperSealed:ident, + ] + $vis:vis trait $HdlCmp:ident<$Rhs:ident: ValueType>: + ValueType $(+ $HdlCmpImplBase:ident)?> $(+ $HdlCmpBase:ident)? + { + $(type $Output:ident: ValueType;)? + $(#[ + helper = $cmp_fn_helper:ident; + value = $cmp_value_fn:ident( + $cmp_value_lhs:ident, + $cmp_value_lhs_value:ident, + $cmp_value_rhs:ident, + $cmp_value_rhs_value:ident $(,)? + ) $cmp_value_body:tt + sim_value = $cmp_sim_value_fn:ident; + expr = $cmp_expr_fn:ident($cmp_expr_lhs:ident, $cmp_expr_rhs:ident) $cmp_expr_body:tt + valueless = $cmp_valueless_fn:ident; + ] + fn $cmp_fn:ident(&self, rhs: Rhs) -> Self::Output;)+ + } + ) => { + $vis trait $HdlCmp<$Rhs: ValueType>: + ValueType> $(+ $HdlCmpBase<$Rhs>)? + { + $(type $Output: ValueType;)? + $(fn $cmp_fn(&self, rhs: $Rhs) -> Self::Output;)+ + } + + $vis trait $HdlCmpImpl<$Rhs: Type>: Type $(+ $HdlCmpImplBase<$Rhs>)? { + $(#[track_caller] + fn $cmp_value_fn( + $cmp_value_lhs: Self, + $cmp_value_lhs_value: Cow<'_, Self::SimValue>, + $cmp_value_rhs: $Rhs, + $cmp_value_rhs_value: Cow<'_, $Rhs::SimValue>, + ) -> bool + $cmp_value_body)+ + + $(#[track_caller] + fn $cmp_sim_value_fn(lhs: Cow<'_, SimValue>, rhs: Cow<'_, SimValue<$Rhs>>) -> SimValue { + let lhs_ty = lhs.ty(); + let rhs_ty = rhs.ty(); + let lhs = match lhs { + Cow::Borrowed(v) => Cow::Borrowed(&**v), + Cow::Owned(v) => Cow::Owned(SimValue::into_value(v)), + }; + let rhs = match rhs { + Cow::Borrowed(v) => Cow::Borrowed(&**v), + Cow::Owned(v) => Cow::Owned(SimValue::into_value(v)), + }; + Self::$cmp_value_fn(lhs_ty, lhs, rhs_ty, rhs).to_sim_value() + })+ + + $(#[track_caller] + fn $cmp_expr_fn($cmp_expr_lhs: Expr, $cmp_expr_rhs: Expr<$Rhs>) -> Expr + $cmp_expr_body)+ + + $(#[track_caller] + fn $cmp_valueless_fn(lhs: Valueless, rhs: Valueless<$Rhs>) -> Valueless { + let _ = lhs; + let _ = rhs; + Valueless::new(Bool) + })+ + } + + trait $HdlCmpImplHelperSealed<$Rhs, Common> {} + + #[expect(private_bounds)] + $vis trait $HdlCmpImplHelper<$Rhs: ValueType, Common: value_category::ValueCategory>: + $HdlCmpImplHelperSealed<$Rhs, Common> + + ValueType> + $(+ $HdlCmpImplHelperBase<$Rhs, Common>)? + { + $(type $Output: ValueType;)? + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: $Rhs) -> Self::Output;)+ + } + + impl $HdlCmp<$Rhs> for Lhs + where + Lhs: ?Sized + ValueType $(+ $HdlCmpBase<$Rhs>)?, + $Rhs: ValueType, + LTy: Type + $HdlCmpImpl, + RTy: Type, + LC: value_category::ValueCategory + value_category::ValueCategoryCommon<(RC,), Common = Common>, + RC: value_category::ValueCategory, + Common: value_category::ValueCategory, + Lhs: $HdlCmpImplHelper<$Rhs, Common> $(+ $HdlCmpImplHelperBase<$Rhs, Common, Output = Self::Output>)?, + { + $(type $Output = >::Output;)? + + $(#[track_caller] + fn $cmp_fn(&self, rhs: $Rhs) -> Self::Output { + >::$cmp_fn_helper(self, rhs) + })+ + } + + impl<'l, 'r, Lhs, $Rhs, LTy, RTy> $HdlCmpImplHelperSealed<$Rhs, value_category::ValueCategoryValue> for Lhs + where + Lhs: ?Sized + ValueType + ToSimValueInner<'l> + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryValue, Output = bool>)?, + $Rhs: ValueType + ToSimValueInner<'r>, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + } + + impl<'l, 'r, Lhs, $Rhs, LTy, RTy> $HdlCmpImplHelper<$Rhs, value_category::ValueCategoryValue> for Lhs + where + Lhs: ?Sized + ValueType + ToSimValueInner<'l> + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryValue, Output = bool>)?, + $Rhs: ValueType + ToSimValueInner<'r>, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + $(type $Output = bool;)? + + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: Rhs) -> Self::Output { + $HdlCmpImpl::$cmp_value_fn( + self.ty(), + Lhs::to_sim_value_inner(self), + rhs.ty(), + $Rhs::into_sim_value_inner(rhs), + ) + })+ + } + + impl $HdlCmpImplHelperSealed<$Rhs, value_category::ValueCategorySimValue> for Lhs + where + Lhs: ?Sized + ValueType + ToSimValue + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategorySimValue, Output = SimValue>)?, + $Rhs: ValueType + ToSimValue, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + } + + impl $HdlCmpImplHelper<$Rhs, value_category::ValueCategorySimValue> for Lhs + where + Lhs: ?Sized + ValueType + ToSimValue + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategorySimValue, Output = SimValue>)?, + $Rhs: ValueType + ToSimValue, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + $(type $Output = SimValue;)? + + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: Rhs) -> Self::Output { + $HdlCmpImpl::$cmp_sim_value_fn(Cow::Owned(Lhs::to_sim_value(self)), Cow::Owned($Rhs::into_sim_value(rhs))) + })+ + } + + impl $HdlCmpImplHelperSealed<$Rhs, value_category::ValueCategoryExpr> for Lhs + where + Lhs: ?Sized + ValueType + ToExpr + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryExpr, Output = Expr>)?, + $Rhs: ValueType + ToExpr, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + } + + impl $HdlCmpImplHelper<$Rhs, value_category::ValueCategoryExpr> for Lhs + where + Lhs: ?Sized + ValueType + ToExpr + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryExpr, Output = Expr>)?, + $Rhs: ValueType + ToExpr, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + $(type $Output = Expr;)? + + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: Rhs) -> Self::Output { + $HdlCmpImpl::$cmp_expr_fn(Lhs::to_expr(self), $Rhs::to_expr(&rhs)) + })+ + } + + impl $HdlCmpImplHelperSealed<$Rhs, value_category::ValueCategoryValueless> for Lhs + where + Lhs: ?Sized + ValueType + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryValueless, Output = Valueless>)?, + $Rhs: ValueType, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + } + + impl $HdlCmpImplHelper<$Rhs, value_category::ValueCategoryValueless> for Lhs + where + Lhs: ?Sized + ValueType + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryValueless, Output = Valueless>)?, + $Rhs: ValueType, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + $(type $Output = Valueless;)? + + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: Rhs) -> Self::Output { + $HdlCmpImpl::$cmp_valueless_fn(Lhs::to_valueless(self), $Rhs::to_valueless(&rhs)) + })+ + } + }; } -pub trait HdlPartialOrd: HdlPartialEq { - fn cmp_lt(self, rhs: Rhs) -> Expr; - fn cmp_le(self, rhs: Rhs) -> Expr; - fn cmp_gt(self, rhs: Rhs) -> Expr; - fn cmp_ge(self, rhs: Rhs) -> Expr; -} - -pub trait ReduceBits { - type UIntOutput; - type BoolOutput; - fn reduce_bitand(self) -> Self::UIntOutput; - fn reduce_bitor(self) -> Self::UIntOutput; - fn reduce_bitxor(self) -> Self::UIntOutput; - fn any_one_bits(self) -> Self::BoolOutput; - fn any_zero_bits(self) -> Self::BoolOutput; - fn all_one_bits(self) -> Self::BoolOutput; - fn all_zero_bits(self) -> Self::BoolOutput; - fn parity_odd(self) -> Self::BoolOutput; - fn parity_even(self) -> Self::BoolOutput; -} - -pub trait CastToBits { - fn cast_to_bits(&self) -> Expr; -} - -impl CastToBits for T { - fn cast_to_bits(&self) -> Expr { - ops::CastToBits::new(Expr::canonical(self.to_expr())).to_expr() +impl_hdl_cmp! { + #[ + impl_helper = HdlPartialEqImplHelper, + impl_helper_sealed = HdlPartialEqImplHelperSealed, + ] + pub trait HdlPartialEq: + ValueType > + { + type Output: ValueType; + #[ + helper = cmp_eq_helper; + value = cmp_value_eq(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_eq; + expr = cmp_expr_eq(lhs, rhs); + valueless = cmp_valueless_eq; + ] + fn cmp_eq(&self, rhs: Rhs) -> Self::Output; + #[ + helper = cmp_ne_helper; + value = cmp_value_ne(lhs, lhs_value, rhs, rhs_value) { + !Self::cmp_value_eq(lhs, lhs_value, rhs, rhs_value) + } + sim_value = cmp_sim_value_ne; + expr = cmp_expr_ne(lhs, rhs) { + !Self::cmp_expr_eq(lhs, rhs) + } + valueless = cmp_valueless_ne; + ] + fn cmp_ne(&self, rhs: Rhs) -> Self::Output; } } -pub trait CastBitsTo { +impl_hdl_cmp! { + #[ + impl_helper = HdlPartialOrdImplHelper, + impl_helper_base = HdlPartialEqImplHelper, + impl_helper_sealed = HdlPartialOrdImplHelperSealed, + ] + pub trait HdlPartialOrd: + ValueType + HdlPartialEqImpl > + + HdlPartialEq + { + #[ + helper = cmp_lt_helper; + value = cmp_value_lt(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_lt; + expr = cmp_expr_lt(lhs, rhs); + valueless = cmp_valueless_lt; + ] + fn cmp_lt(&self, rhs: Rhs) -> Self::Output; + #[ + helper = cmp_le_helper; + value = cmp_value_le(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_le; + expr = cmp_expr_le(lhs, rhs); + valueless = cmp_valueless_le; + ] + fn cmp_le(&self, rhs: Rhs) -> Self::Output; + #[ + helper = cmp_gt_helper; + value = cmp_value_gt(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_gt; + expr = cmp_expr_gt(lhs, rhs); + valueless = cmp_valueless_gt; + ] + fn cmp_gt(&self, rhs: Rhs) -> Self::Output; + #[ + helper = cmp_ge_helper; + value = cmp_value_ge(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_ge; + expr = cmp_expr_ge(lhs, rhs); + valueless = cmp_valueless_ge; + ] + fn cmp_ge(&self, rhs: Rhs) -> Self::Output; + } +} + +macro_rules! make_reduce_bits_traits { + ( + $vis:vis trait $ReduceBits:ident { + $(type $Output:ident: ValueType;)* + $(#[ + ty = $ty:ty, + value_ty = $value_ty:ty, + value = $value_f:ident($value_this:ident, $value_value:ident) $value_body:tt + sim_value = $sim_value_f:ident, + expr = $expr_f:ident($expr_this:ident) $expr_body:tt + valueless = $valueless_f:ident, + ] + fn $f:ident(&self) -> Self::$FnOutput:ident;)* + } + ) => { + $vis trait $ReduceBits { + $(type $Output: ValueType;)* + $(fn $f(&self) -> Self::$FnOutput;)* + } + + trait ReduceBitsImplHelperSealed {} + + #[expect(private_bounds)] + $vis trait ReduceBitsImplHelper: ReduceBitsImplHelperSealed { + $(type $Output: ValueType;)* + $(fn $f(this: &Self) -> Self::$FnOutput;)* + } + + impl::ValueCategory>> $ReduceBits for T { + $(type $Output = ::ValueCategory>>::$Output;)* + $(fn $f(&self) -> Self::$FnOutput { + ::ValueCategory>>::$f(self) + })* + } + + $vis trait ReduceBitsImpl: Type { + $(fn $value_f($value_this: Self, $value_value: Cow<'_, Self::SimValue>) -> $value_ty $value_body)* + + $(fn $sim_value_f(this: Cow<'_, SimValue>) -> SimValue<$ty> { + let ty = this.ty(); + let value = match this { + Cow::Borrowed(v) => Cow::Borrowed(SimValue::value(v)), + Cow::Owned(v) => Cow::Owned(SimValue::into_value(v)), + }; + Self::$value_f(ty, value).into_sim_value() + })* + + $(fn $expr_f($expr_this: Expr) -> Expr<$ty> $expr_body)* + + $(fn $valueless_f(_this: Valueless) -> Valueless<$ty> { + Valueless::new(StaticType::TYPE) + })* + } + + impl<'a, T> ReduceBitsImplHelperSealed for T + where + T: ?Sized + ValueType + ToSimValueInner<'a>, + { + } + + impl<'a, T> ReduceBitsImplHelper for T + where + T: ?Sized + ValueType + ToSimValueInner<'a>, + { + type UIntOutput = UIntValue>; + type BoolOutput = bool; + + $(fn $f(this: &Self) -> Self::$FnOutput { + T::Type::$value_f(this.ty(), Self::to_sim_value_inner(this)) + })* + } + + impl<'a, T> ReduceBitsImplHelperSealed for T + where + T: ?Sized + ValueType + ToSimValue, + { + } + + impl<'a, T> ReduceBitsImplHelper for T + where + T: ?Sized + ValueType + ToSimValue, + { + type UIntOutput = SimValue>; + type BoolOutput = SimValue; + + $(fn $f(this: &Self) -> Self::$FnOutput { + T::Type::$sim_value_f(Cow::Owned(this.to_sim_value())) + })* + } + + impl<'a, T> ReduceBitsImplHelperSealed for T + where + T: ?Sized + ValueType + ToExpr, + { + } + + impl<'a, T> ReduceBitsImplHelper for T + where + T: ?Sized + ValueType + ToExpr, + { + type UIntOutput = Expr>; + type BoolOutput = Expr; + + $(fn $f(this: &Self) -> Self::$FnOutput { + T::Type::$expr_f(this.to_expr()) + })* + } + + impl<'a, T> ReduceBitsImplHelperSealed for T + where + T: ?Sized + ValueType, + { + } + + impl<'a, T> ReduceBitsImplHelper for T + where + T: ?Sized + ValueType, + { + type UIntOutput = Valueless>; + type BoolOutput = Valueless; + + $(fn $f(this: &Self) -> Self::$FnOutput { + T::Type::$valueless_f(this.to_valueless()) + })* + } + }; +} + +make_reduce_bits_traits! { + pub trait ReduceBits { + type UIntOutput: ValueType>; + type BoolOutput: ValueType; + + #[ + ty = UInt<1>, + value_ty = UIntValue>, + value = value_reduce_bitand(this, value); + sim_value = sim_value_reduce_bitand, + expr = expr_reduce_bitand(this); + valueless = valueless_reduce_bitand, + ] + fn reduce_bitand(&self) -> Self::UIntOutput; + + #[ + ty = UInt<1>, + value_ty = UIntValue>, + value = value_reduce_bitor(this, value); + sim_value = sim_value_reduce_bitor, + expr = expr_reduce_bitor(this); + valueless = valueless_reduce_bitor, + ] + fn reduce_bitor(&self) -> Self::UIntOutput; + + #[ + ty = UInt<1>, + value_ty = UIntValue>, + value = value_reduce_bitxor(this, value); + sim_value = sim_value_reduce_bitxor, + expr = expr_reduce_bitxor(this); + valueless = valueless_reduce_bitxor, + ] + fn reduce_bitxor(&self) -> Self::UIntOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_any_one_bits(this, value) { + Self::value_reduce_bitor(this, value).bits()[0] + } + sim_value = sim_value_any_one_bits, + expr = expr_any_one_bits(this) { + this.reduce_bitor().cast_to_static() + } + valueless = valueless_any_one_bits, + ] + fn any_one_bits(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_any_zero_bits(this, value) { + !Self::value_reduce_bitand(this, value).bits()[0] + } + sim_value = sim_value_any_zero_bits, + expr = expr_any_zero_bits(this) { + (!this.reduce_bitand()).cast_to_static() + } + valueless = valueless_any_zero_bits, + ] + fn any_zero_bits(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_all_one_bits(this, value) { + Self::value_reduce_bitand(this, value).bits()[0] + } + sim_value = sim_value_all_one_bits, + expr = expr_all_one_bits(this) { + this.reduce_bitand().cast_to_static() + } + valueless = valueless_all_one_bits, + ] + fn all_one_bits(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_all_zero_bits(this, value) { + !Self::value_reduce_bitor(this, value).bits()[0] + } + sim_value = sim_value_all_zero_bits, + expr = expr_all_zero_bits(this) { + (!this.reduce_bitor()).cast_to_static() + } + valueless = valueless_all_zero_bits, + ] + fn all_zero_bits(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_parity_odd(this, value) { + Self::value_reduce_bitxor(this, value).bits()[0] + } + sim_value = sim_value_parity_odd, + expr = expr_parity_odd(this) { + this.reduce_bitxor().cast_to_static() + } + valueless = valueless_parity_odd, + ] + fn parity_odd(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_parity_even(this, value) { + !Self::value_reduce_bitxor(this, value).bits()[0] + } + sim_value = sim_value_parity_even, + expr = expr_parity_even(this) { + (!this.reduce_bitxor()).cast_to_static() + } + valueless = valueless_parity_even, + ] + fn parity_even(&self) -> Self::BoolOutput; + } +} + +pub trait CastToBits: ValueType { + type Output: ValueType; + fn cast_to_bits(&self) -> Self::Output; +} + +impl> CastToBits for T { + type Output = T::ImplOutput; + fn cast_to_bits(&self) -> Self::Output { + Self::cast_to_bits_impl(self) + } +} + +pub trait CastToBitsImpl { + type ImplOutput: ValueType; + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput; +} + +impl + CastToBitsImpl for T +{ + type ImplOutput = UIntValue; + + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput { + crate::sim::value::SimValue::bits(&this.to_sim_value()).clone() + } +} + +impl + CastToBitsImpl for T +{ + type ImplOutput = SimValue; + + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput { + crate::sim::value::SimValue::bits(&this.to_sim_value()).to_sim_value() + } +} + +impl CastToBitsImpl for T { + type ImplOutput = Expr; + + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput { + ops::CastToBits::new(Expr::canonical(this.to_expr())).to_expr() + } +} + +impl CastToBitsImpl for T { + type ImplOutput = Valueless; + + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput { + Valueless::new(UInt::new_dyn(this.ty().canonical().bit_width())) + } +} + +pub trait CastBitsTo: ValueType>> { + type Output: ValueType; + #[track_caller] - fn cast_bits_to(&self, ty: T) -> Expr; + fn cast_bits_to(&self, ty: T) -> Self::Output; } -impl> + ?Sized, Width: Size> CastBitsTo for T { - fn cast_bits_to(&self, ty: ToType) -> Expr { - ops::CastBitsTo::new(Expr::as_dyn_int(self.to_expr()), ty).to_expr() +impl< + This: ?Sized + ValueType> + CastBitsToImpl, + Width: Size, +> CastBitsTo for This +{ + type Output = This::ImplOutput; + + #[track_caller] + fn cast_bits_to(&self, ty: T) -> Self::Output { + Self::cast_bits_to_impl(self, ty) } } -pub trait CastTo: ToExpr { - fn cast_to(&self, to_type: ToType) -> Expr +pub trait CastBitsToImpl { + type ImplOutput: ValueType; + + #[track_caller] + fn cast_bits_to_impl(this: &Self, ty: T) -> Self::ImplOutput; +} + +impl< + 'a, + This: ?Sized + ValueType> + ToSimValueInner<'a>, + Width: Size, + C: value_category::ValueCategoryIsValueOrSimValue, +> CastBitsToImpl for This +{ + type ImplOutput = SimValue; + + #[track_caller] + fn cast_bits_to_impl(this: &Self, ty: T) -> Self::ImplOutput { + let ty_props = ty.canonical().type_properties(); + assert!(ty_props.is_castable_from_bits); + assert_eq!(this.ty().width(), ty_props.bit_width); + crate::sim::value::SimValue::from_opaque( + ty, + OpaqueSimValue::from_bits(Self::to_sim_value_inner(this).into_owned().as_dyn_int()), + ) + } +} + +impl> + ToExpr, Width: Size> + CastBitsToImpl for This +{ + type ImplOutput = Expr; + + #[track_caller] + fn cast_bits_to_impl(this: &Self, ty: T) -> Self::ImplOutput { + ops::CastBitsTo::new(Expr::as_dyn_int(this.to_expr()), ty).to_expr() + } +} + +impl>, Width: Size> + CastBitsToImpl for This +{ + type ImplOutput = Valueless; + + #[track_caller] + fn cast_bits_to_impl(this: &Self, ty: T) -> Self::ImplOutput { + let ty_props = ty.canonical().type_properties(); + assert!(ty_props.is_castable_from_bits); + assert_eq!(this.ty().width(), ty_props.bit_width); + Valueless::new(ty) + } +} + +pub trait CastToImpl: Type { + type ValueOutput: ValueType + + ToSimValueWithType; + #[track_caller] + fn cast_value_to( + this: Self, + value: Cow<'_, Self::SimValue>, + to_type: ToType, + ) -> Self::ValueOutput; + #[track_caller] + fn cast_sim_value_to(value: Cow<'_, SimValue>, to_type: ToType) -> SimValue { + let ty = value.ty(); + let value = match value { + Cow::Borrowed(value) => Cow::Borrowed(&**value), + Cow::Owned(value) => Cow::Owned(SimValue::into_value(value)), + }; + Self::cast_value_to(ty, value, to_type).into_sim_value_with_type(to_type) + } + #[track_caller] + fn cast_expr_to(value: Expr, to_type: ToType) -> Expr; + #[track_caller] + fn cast_valueless_to(value: Valueless, to_type: ToType) -> Valueless { + let _ = value; + Valueless::new(to_type) + } +} + +trait CastToImplHelperSealed {} + +#[expect(private_bounds)] +pub trait CastToImplHelper: + ValueType + CastToImplHelperSealed +{ + type Output: ValueType where - Self::Type: ExprCastTo, - { - ExprCastTo::cast_to(self.to_expr(), to_type) - } - fn cast_to_static(&self) -> Expr + Self::Type: CastToImpl; + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output where - Self::Type: ExprCastTo, + Self::Type: CastToImpl; +} + +impl< + 'a, + This: ?Sized + ValueType + ToSimValueInner<'a>, +> CastToImplHelperSealed for This +{ +} + +impl< + 'a, + This: ?Sized + ValueType + ToSimValueInner<'a>, +> CastToImplHelper for This +{ + type Output + = >::ValueOutput + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, { - ExprCastTo::cast_to(self.to_expr(), ToType::TYPE) + Self::Type::cast_value_to(self.ty(), Self::to_sim_value_inner(self), to_type) } } -impl CastTo for T {} +impl + ToSimValue> + CastToImplHelperSealed for This +{ +} + +impl + ToSimValue> + CastToImplHelper for This +{ + type Output + = SimValue + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, + { + Self::Type::cast_sim_value_to(Cow::Owned(self.to_sim_value()), to_type) + } +} + +impl + ToExpr> + CastToImplHelperSealed for This +{ +} + +impl + ToExpr> + CastToImplHelper for This +{ + type Output + = Expr + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, + { + Self::Type::cast_expr_to(self.to_expr(), to_type) + } +} + +impl> + CastToImplHelperSealed for This +{ +} + +impl> + CastToImplHelper for This +{ + type Output + = Valueless + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, + { + Self::Type::cast_valueless_to(self.to_valueless(), to_type) + } +} + +pub trait CastTo: ValueType { + type Output: ValueType + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_static(&self) -> Self::Output + where + Self::Type: CastToImpl, + { + self.cast_to(ToType::TYPE) + } +} + +impl< + T: ValueType + CastToImplHelper + ?Sized, + C: value_category::ValueCategory, +> CastTo for T +{ + type Output + = >::Output + where + Self::Type: CastToImpl; + fn cast_to(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, + { + Self::cast_to_impl_helper(self, to_type) + } +} #[doc(hidden)] pub fn check_match_expr( @@ -761,7 +1520,7 @@ pub fn repeat( let element = element.to_expr(); let canonical_element = Expr::canonical(element); ops::ArrayLiteral::new( - Expr::ty(element), + element.ty(), std::iter::repeat(canonical_element) .take(L::Size::as_usize(len)) .collect(), @@ -769,9 +1528,16 @@ pub fn repeat( .to_expr() } -impl ToExpr for PhantomConst { +impl ValueType for PhantomConst { type Type = Self; + type ValueCategory = value_category::ValueCategoryValue; + fn ty(&self) -> Self::Type { + *self + } +} + +impl ToExpr for PhantomConst { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(), @@ -792,3 +1558,137 @@ impl ToLiteralBits for Phan Ok(Interned::default()) } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Valueless(T); + +impl Valueless { + pub const fn new(ty: T) -> Self { + Self(ty) + } + pub const fn ty(self) -> T { + self.0 + } +} + +impl Default for Valueless { + fn default() -> Self { + Self(T::TYPE) + } +} + +pub trait ValueType { + type Type: Type; + type ValueCategory: value_category::ValueCategory; + fn ty(&self) -> Self::Type; +} + +trait ToValuelessSealed {} + +#[expect(private_bounds)] +pub trait ToValueless: ValueType + ToValuelessSealed { + fn to_valueless(&self) -> Valueless { + Valueless::new(self.ty()) + } +} + +impl ToValuelessSealed for T {} + +impl ToValueless for T {} + +impl ValueType for Valueless { + type Type = T; + type ValueCategory = value_category::ValueCategoryValueless; + + fn ty(&self) -> Self::Type { + self.0 + } +} + +impl<'a, T: ?Sized + ValueType> ValueType for &'a T { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl<'a, T: ?Sized + ValueType> ValueType for &'a mut T { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for Box { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for std::sync::Arc { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for std::rc::Rc { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for Interned { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for Expr { + type Type = T; + type ValueCategory = value_category::ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.__ty + } +} + +pub trait ToSimValueInner<'a>: ValueType { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue>; + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> + where + Self: Sized; +} + +impl<'a, T: Type> ToSimValueInner<'a> for SimValue { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Borrowed(&**this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(SimValue::into_value(this)) + } +} + +impl<'a, T: Type> ToSimValueInner<'a> for &'a SimValue { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Borrowed(&***this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Borrowed(&**this) + } +} diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index b10e3ae8..b8ef4f72 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -7,12 +7,14 @@ use crate::{ clock::{Clock, ToClock}, enum_::{Enum, EnumType, EnumVariant}, expr::{ - CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq, - HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, + CastBitsTo as _, CastTo, CastToBits as _, CastToImpl, Expr, ExprEnum, Flow, HdlPartialEq, + HdlPartialEqImpl, HdlPartialOrd, HdlPartialOrdImpl, NotALiteralExpr, ReduceBitsImpl, + ToExpr, ToLiteralBits, ToSimValueInner, ToValueless, ValueType, Valueless, target::{ GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, }, + value_category::ValueCategoryExpr, }, int::{ Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, @@ -24,6 +26,7 @@ use crate::{ AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, ToSyncReset, }, + sim::value::{SimValue, ToSimValue}, ty::{CanonicalType, StaticType, Type}, util::ConstUsize, }; @@ -31,50 +34,1759 @@ use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; use num_bigint::BigInt; use num_traits::{ToPrimitive, Zero}; use std::{ + borrow::Cow, fmt, marker::PhantomData, + num::NonZero, ops::{ Add, BitAnd, BitOr, BitXor, Div, Index, Mul, Neg as StdNeg, Not, Range, RangeBounds, Rem, Shl, Shr, Sub, }, }; -macro_rules! forward_value_to_expr_unary_op_trait { +macro_rules! make_impls { ( - #[generics($($generics:tt)*)] - #[value($Value:ty)] - $Trait:ident::$method:ident + $([$($args:tt)*])? + $m:ident! {$($rest:tt)*} ) => { - impl<$($generics)*> $Trait for $Value - where - Expr<<$Value as ToExpr>::Type>: $Trait, - { - type Output = ::Type> as $Trait>::Output; + $m! {$($($args)*)? $($rest)*} + }; + ( + #[kinds()] + $($rest:tt)* + ) => {}; + ( + #[kinds($first_kind:tt $(, $kinds:tt)* $(,)?)] + $($rest:tt)* + ) => { + make_impls! { + #[kind($first_kind)] + $($rest)* + } + make_impls! { + #[kinds($($kinds),*)] + $($rest)* + } + }; + ( + #[type($($ty:tt)*)] + $(#[$($meta:tt)*])* + $([$($args:tt)*])? + $m:ident $($rest:tt)* + ) => { + make_impls! { + $(#[$($meta)*])* + [$($($args)*)? $($ty)*,] + $m $($rest)* + } + }; + ( + #[kind((int_no_nz<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((uint_no_nz<$lt, $Width>), (sint_no_nz<$lt, $Width>))] + $($rest)* + } + }; + ( + #[kind((int_local<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((uint_local<$lt, $Width>), (sint_local<$lt, $Width>))] + $($rest)* + } + }; + ( + #[kind((int_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((uint_value<$lt, $Width>), (sint_value<$lt, $Width>))] + $($rest)* + } + }; + ( + #[kind((int_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((uint_sim_value<$lt, $Width>), (sint_sim_value<$lt, $Width>))] + $($rest)* + } + }; + ( + #[kind((uint<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (NonZero), + NonZeroU_N, + (uint_no_nz<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((sint<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (NonZero), + NonZeroI_N, + (sint_no_nz<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((uint_no_nz<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + usize, + u_N, + (uint_value<$lt, $Width>), + (uint_sim_value<$lt, $Width>), + (Expr>), + (Valueless>), + )] + $($rest)* + } + }; + ( + #[kind((sint_no_nz<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + isize, + i_N, + (sint_value<$lt, $Width>), + (sint_sim_value<$lt, $Width>), + (Expr>), + (Valueless>), + )] + $($rest)* + } + }; + ( + #[kind((uint_at_most_expr<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_at_most_sim_value<$lt, $Width>), + (Expr>), + )] + $($rest)* + } + }; + ( + #[kind((sint_at_most_expr<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_at_most_sim_value<$lt, $Width>), + (Expr>), + )] + $($rest)* + } + }; + ( + #[kind((uint_at_most_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_at_most_value<$lt, $Width>), + (uint_sim_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((sint_at_most_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_at_most_value<$lt, $Width>), + (sint_sim_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((uint_local_at_most_expr<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_local_at_most_sim_value<$lt, $Width>), + (Expr>), + )] + $($rest)* + } + }; + ( + #[kind((sint_local_at_most_expr<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_local_at_most_sim_value<$lt, $Width>), + (Expr>), + )] + $($rest)* + } + }; + ( + #[kind((uint_local_at_most_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_value<$lt, $Width>), + (uint_sim_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((sint_local_at_most_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_value<$lt, $Width>), + (sint_sim_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((uint_at_most_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + prim_uint, + (uint_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((sint_at_most_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + prim_sint, + (sint_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind(prim_uint)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (NonZero), + NonZeroU_N, + usize, + u_N, + )] + $($rest)* + } + }; + ( + #[kind(prim_sint)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (NonZero), + NonZeroI_N, + isize, + i_N, + )] + $($rest)* + } + }; + ( + #[kind((bool_local_at_most_expr<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (bool_sim_value<$lt>), + (Expr), + )] + $($rest)* + } + }; + ( + #[kind((bool_at_most_expr<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (bool_at_most_sim_value<$lt>), + (Expr), + )] + $($rest)* + } + }; + ( + #[kind((bool_at_most_sim_value<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + bool, + (bool_sim_value<$lt>), + )] + $($rest)* + } + }; + ( + #[kind((uint_local<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_value<$lt, $Width>), + (SimValue>), + (&$lt SimValue>), + (Expr>), + (Valueless>), + )] + $($rest)* + } + }; + ( + #[kind((sint_local<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_value<$lt, $Width>), + (SimValue>), + (&$lt SimValue>), + (Expr>), + (Valueless>), + )] + $($rest)* + } + }; + ( + #[kind((uint_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (UIntValue<$Width: Size>), + (&$lt UIntValue<$Width: Size>), + )] + $($rest)* + } + }; + ( + #[kind((sint_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (SIntValue<$Width: Size>), + (&$lt SIntValue<$Width: Size>), + )] + $($rest)* + } + }; + ( + #[kind((uint_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (SimValue>), + (&$lt SimValue>), + )] + $($rest)* + } + }; + ( + #[kind((sint_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (SimValue>), + (&$lt SimValue>), + )] + $($rest)* + } + }; + ( + #[kind((bool_sim_value<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (SimValue), + (&$lt SimValue), + )] + $($rest)* + } + }; + ( + #[kind((bool<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + bool, + (bool_sim_value<$lt>), + (Expr), + (Valueless), + )] + $($rest)* + } + }; + ( + #[kind((bool_local<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (bool_sim_value<$lt>), + (Expr), + (Valueless), + )] + $($rest)* + } + }; + ( + #[kind(u_N)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds(u8, u16, u32, u64, u128)] + $($rest)* + } + }; + ( + #[kind(i_N)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds(i8, i16, i32, i64, i128)] + $($rest)* + } + }; + ( + #[kind(NonZeroU_N)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((NonZero), (NonZero), (NonZero), (NonZero), (NonZero))] + $($rest)* + } + }; + ( + #[kind(NonZeroI_N)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((NonZero), (NonZero), (NonZero), (NonZero), (NonZero))] + $($rest)* + } + }; + ( + #[kind(($wrapper:ident<$Ty:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([][] ($wrapper<$Ty>))] + $($rest)* + } + }; + ( + #[kind((&$lt:lifetime $wrapper:ident<$Ty:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([$lt,][] (&$lt $wrapper<$Ty>))] + $($rest)* + } + }; + ( + #[kind(($wrapper:ident<$Width:ident: Size>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([][$Width: Size,] ($wrapper<$Width>))] + $($rest)* + } + }; + ( + #[kind(($wrapper:ident<$ty:ident<$Width:ident: Size>>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([][$Width: Size,] ($wrapper<$ty<$Width>>))] + $($rest)* + } + }; + ( + #[kind((&$lt:lifetime $wrapper:ident<$Width:ident: Size>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([$lt,][$Width: Size,] (&$lt $wrapper<$Width>))] + $($rest)* + } + }; + ( + #[kind((&$lt:lifetime $wrapper:ident<$ty:ident<$Width:ident: Size>>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([$lt,][$Width: Size,] (&$lt $wrapper<$ty<$Width>>))] + $($rest)* + } + }; + (#[kind(usize)] $($rest:tt)*) => {make_impls! { #[type([][] (usize))] $($rest)* }}; + (#[kind(isize)] $($rest:tt)*) => {make_impls! { #[type([][] (isize))] $($rest)* }}; + (#[kind(bool)] $($rest:tt)*) => {make_impls! { #[type([][] (bool))] $($rest)* }}; + (#[kind(u8)] $($rest:tt)*) => {make_impls! { #[type([][] (u8))] $($rest)* }}; + (#[kind(u16)] $($rest:tt)*) => {make_impls! { #[type([][] (u16))] $($rest)* }}; + (#[kind(u32)] $($rest:tt)*) => {make_impls! { #[type([][] (u32))] $($rest)* }}; + (#[kind(u64)] $($rest:tt)*) => {make_impls! { #[type([][] (u64))] $($rest)* }}; + (#[kind(u128)] $($rest:tt)*) => {make_impls! { #[type([][] (u128))] $($rest)* }}; + (#[kind(i8)] $($rest:tt)*) => {make_impls! { #[type([][] (i8))] $($rest)* }}; + (#[kind(i16)] $($rest:tt)*) => {make_impls! { #[type([][] (i16))] $($rest)* }}; + (#[kind(i32)] $($rest:tt)*) => {make_impls! { #[type([][] (i32))] $($rest)* }}; + (#[kind(i64)] $($rest:tt)*) => {make_impls! { #[type([][] (i64))] $($rest)* }}; + (#[kind(i128)] $($rest:tt)*) => {make_impls! { #[type([][] (i128))] $($rest)* }}; +} +pub(crate) use make_impls; - fn $method(self) -> Self::Output { - $Trait::$method(self.to_expr()) +#[cfg(test)] +mod test_ops_impls; + +macro_rules! impl_simple_binary_op_trait { + ( + [$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), + [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), + $(#[$meta:meta])* + $Trait:ident::$f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block $(,)? + ) => { + $(#[$meta])* + impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*> $Trait<$($R)*> for $($L)* { + type Output = $Out<<::Type> as $Trait::Type>>>::Output as ValueType>::Type>; + + fn $f($f_self, $f_rhs: $($R)*) -> Self::Output $body + } + }; + ( + $LLifetimes:tt $LBounds:tt ($($L:tt)*), + $RLifetimes:tt $RBounds:tt ($($R:tt)*), + $(#[$meta:meta])* + $FirstTrait:ident::$first_f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block, + $($rest:tt)* + ) => { + impl_simple_binary_op_trait! { + $LLifetimes $LBounds ($($L)*), + $RLifetimes $RBounds ($($R)*), + $(#[$meta])* + $FirstTrait::$first_f($f_self, $f_rhs) -> $Out<_> $body + } + impl_simple_binary_op_trait! { + $LLifetimes $LBounds ($($L)*), + $RLifetimes $RBounds ($($R)*), + $(#[$meta])* + $($rest)* + } + }; +} + +make_impls! { + #[kinds((Expr>))] + #[kinds((uint_at_most_sim_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Expr<_> { + AddU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Sub::sub(self, rhs) -> Expr<_> { + SubU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Mul::mul(self, rhs) -> Expr<_> { + MulU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Div::div(self, rhs) -> Expr<_> { + DivU::new(self.to_expr(), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Rem::rem(self, rhs) -> Expr<_> { + RemU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + #[kinds((sint_at_most_sim_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Expr<_> { + AddS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Sub::sub(self, rhs) -> Expr<_> { + SubS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Mul::mul(self, rhs) -> Expr<_> { + MulS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Div::div(self, rhs) -> Expr<_> { + DivS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Rem::rem(self, rhs) -> Expr<_> { + RemS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_expr<'l, L>))] + #[kinds((Expr>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Expr<_> { + AddU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Sub::sub(self, rhs) -> Expr<_> { + SubU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Mul::mul(self, rhs) -> Expr<_> { + MulU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Div::div(self, rhs) -> Expr<_> { + DivU::new(self.to_expr(), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Rem::rem(self, rhs) -> Expr<_> { + RemU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((sint_at_most_expr<'l, L>))] + #[kinds((Expr>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Expr<_> { + AddS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Sub::sub(self, rhs) -> Expr<_> { + SubS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Mul::mul(self, rhs) -> Expr<_> { + MulS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Div::div(self, rhs) -> Expr<_> { + DivS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Rem::rem(self, rhs) -> Expr<_> { + RemS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((uint_sim_value<'l, L>))] + #[kinds((uint_at_most_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())).into_sim_value() + }, + Sub::sub(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())).into_sim_value() + }, + Mul::mul(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())).into_sim_value() + }, + Div::div(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())).into_sim_value() + }, + Rem::rem(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())).into_sim_value() + }, + BitAnd::bitand(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_sim_value<'l, L>))] + #[kinds((sint_at_most_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())).into_sim_value() + }, + Sub::sub(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())).into_sim_value() + }, + Mul::mul(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())).into_sim_value() + }, + Div::div(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())).into_sim_value() + }, + Rem::rem(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())).into_sim_value() + }, + BitAnd::bitand(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_sim_value<'l, L>))] + #[kinds((uint_sim_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())).into_sim_value() + }, + Sub::sub(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())).into_sim_value() + }, + Mul::mul(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())).into_sim_value() + }, + Div::div(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())).into_sim_value() + }, + Rem::rem(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())).into_sim_value() + }, + BitAnd::bitand(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_at_most_sim_value<'l, L>))] + #[kinds((sint_sim_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())).into_sim_value() + }, + Sub::sub(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())).into_sim_value() + }, + Mul::mul(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())).into_sim_value() + }, + Div::div(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())).into_sim_value() + }, + Rem::rem(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())).into_sim_value() + }, + BitAnd::bitand(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_expr<'l, L>))] + #[kinds((Valueless>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Valueless<_> { + self.to_valueless().add(rhs.to_valueless()) + }, + Sub::sub(self, rhs) -> Valueless<_> { + self.to_valueless().sub(rhs.to_valueless()) + }, + Mul::mul(self, rhs) -> Valueless<_> { + self.to_valueless().mul(rhs.to_valueless()) + }, + Div::div(self, rhs) -> Valueless<_> { + self.to_valueless().div(rhs.to_valueless()) + }, + Rem::rem(self, rhs) -> Valueless<_> { + self.to_valueless().rem(rhs.to_valueless()) + }, + BitAnd::bitand(self, rhs) -> Valueless<_> { + self.to_valueless().bitand(rhs.to_valueless()) + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + self.to_valueless().bitor(rhs.to_valueless()) + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + self.to_valueless().bitxor(rhs.to_valueless()) + }, + } +} + +make_impls! { + #[kinds((sint_at_most_expr<'l, L>))] + #[kinds((Valueless>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Valueless<_> { + self.to_valueless().add(rhs.to_valueless()) + }, + Sub::sub(self, rhs) -> Valueless<_> { + self.to_valueless().sub(rhs.to_valueless()) + }, + Mul::mul(self, rhs) -> Valueless<_> { + self.to_valueless().mul(rhs.to_valueless()) + }, + Div::div(self, rhs) -> Valueless<_> { + self.to_valueless().div(rhs.to_valueless()) + }, + Rem::rem(self, rhs) -> Valueless<_> { + self.to_valueless().rem(rhs.to_valueless()) + }, + BitAnd::bitand(self, rhs) -> Valueless<_> { + self.to_valueless().bitand(rhs.to_valueless()) + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + self.to_valueless().bitor(rhs.to_valueless()) + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + self.to_valueless().bitxor(rhs.to_valueless()) + }, + } +} + +make_impls! { + #[kinds((Valueless>))] + #[kinds((uint_at_most_expr<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Valueless<_> { + self.to_valueless().add(rhs.to_valueless()) + }, + Sub::sub(self, rhs) -> Valueless<_> { + self.to_valueless().sub(rhs.to_valueless()) + }, + Mul::mul(self, rhs) -> Valueless<_> { + self.to_valueless().mul(rhs.to_valueless()) + }, + Div::div(self, rhs) -> Valueless<_> { + self.to_valueless().div(rhs.to_valueless()) + }, + Rem::rem(self, rhs) -> Valueless<_> { + self.to_valueless().rem(rhs.to_valueless()) + }, + BitAnd::bitand(self, rhs) -> Valueless<_> { + self.to_valueless().bitand(rhs.to_valueless()) + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + self.to_valueless().bitor(rhs.to_valueless()) + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + self.to_valueless().bitxor(rhs.to_valueless()) + }, + } +} + +make_impls! { + #[kinds((Valueless>))] + #[kinds((sint_at_most_expr<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Valueless<_> { + self.to_valueless().add(rhs.to_valueless()) + }, + Sub::sub(self, rhs) -> Valueless<_> { + self.to_valueless().sub(rhs.to_valueless()) + }, + Mul::mul(self, rhs) -> Valueless<_> { + self.to_valueless().mul(rhs.to_valueless()) + }, + Div::div(self, rhs) -> Valueless<_> { + self.to_valueless().div(rhs.to_valueless()) + }, + Rem::rem(self, rhs) -> Valueless<_> { + self.to_valueless().rem(rhs.to_valueless()) + }, + BitAnd::bitand(self, rhs) -> Valueless<_> { + self.to_valueless().bitand(rhs.to_valueless()) + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + self.to_valueless().bitor(rhs.to_valueless()) + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + self.to_valueless().bitxor(rhs.to_valueless()) + }, + } +} + +type SimValueInner = ::SimValue; + +trait ValueIntoBigInt { + fn value_to_bigint(&self) -> BigInt; +} + +macro_rules! impl_value_to_bigint { + ([$($Lifetimes:tt)*][$($Bounds:tt)*] (NonZero<$T:ty>),) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for NonZero<$T> { + fn value_to_bigint(&self) -> BigInt { + self.get().into() + } + } + }; + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($(&$lt:lifetime)? $IntValue:ident<$Width:ident>),) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for $(&$lt)? $IntValue<$Width> { + fn value_to_bigint(&self) -> BigInt { + self.to_bigint() + } + } + }; + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($(&$lt:lifetime)? SimValue<$IntType:ident<$Width:ident>>),) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for $(&$lt)? SimValue<$IntType<$Width>> { + fn value_to_bigint(&self) -> BigInt { + self.to_bigint() + } + } + }; + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($T:ty),) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for $T { + fn value_to_bigint(&self) -> BigInt { + (*self).into() } } }; } -macro_rules! impl_unary_op_trait { - ( - #[generics($($generics:tt)*)] - fn $Trait:ident::$method:ident($arg:ident: $Arg:ty) -> $Output:ty { - $($body:tt)* - } - ) => { - impl<$($generics)*> $Trait for Expr<$Arg> - { - type Output = Expr<$Output>; +make_impls! { + #[kinds((sint_at_most_sim_value<'a, Width>), (uint_at_most_sim_value<'a, Width>))] + impl_value_to_bigint! {} +} - fn $method(self) -> Self::Output { - let $arg = self; - $($body)* - } +make_impls! { + #[kinds((uint_value<'l, L>))] + #[kinds(prim_uint)] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())) + }, + Sub::sub(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())) + }, + Mul::mul(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())) + }, + Div::div(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())) + }, + Rem::rem(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())) + }, + BitAnd::bitand(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())) + }, + BitOr::bitor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())) + }, + BitXor::bitxor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())) + }, + } +} + +make_impls! { + #[kinds((sint_value<'l, L>))] + #[kinds(prim_sint)] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())) + }, + Sub::sub(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())) + }, + Mul::mul(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())) + }, + Div::div(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())) + }, + Rem::rem(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())) + }, + BitAnd::bitand(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())) + }, + BitOr::bitor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())) + }, + BitXor::bitxor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())) + }, + } +} + +make_impls! { + #[kinds((uint_at_most_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())) + }, + Sub::sub(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())) + }, + Mul::mul(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())) + }, + Div::div(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())) + }, + Rem::rem(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())) + }, + BitAnd::bitand(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())) + }, + BitOr::bitor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())) + }, + BitXor::bitxor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())) + }, + } +} + +make_impls! { + #[kinds((sint_at_most_value<'l, L>))] + #[kinds((sint_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())) + }, + Sub::sub(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())) + }, + Mul::mul(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())) + }, + Div::div(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())) + }, + Rem::rem(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())) + }, + BitAnd::bitand(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())) + }, + BitOr::bitor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())) + }, + BitXor::bitxor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())) + }, + } +} + +make_impls! { + #[kinds((Expr))] + #[kinds((bool_sim_value<'r>))] + impl_simple_binary_op_trait! { + /// intentionally only implemented for expressions, not rust's bool type, + /// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do + /// what is usually wanted. + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + } +} + +make_impls! { + #[kinds((bool_local_at_most_expr<'l>))] + #[kinds((Expr))] + impl_simple_binary_op_trait! { + /// intentionally only implemented for expressions, not rust's bool type, + /// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do + /// what is usually wanted. + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + } +} + +make_impls! { + #[kinds((bool_at_most_sim_value<'l>))] + #[kinds((bool_sim_value<'r>))] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitand(*ToSimValueInner::into_sim_value_inner(rhs)).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitor(*ToSimValueInner::into_sim_value_inner(rhs)).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitxor(*ToSimValueInner::into_sim_value_inner(rhs)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((bool_sim_value<'l>))] + #[kinds(bool)] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitand(rhs).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitor(rhs).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitxor(rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((bool_at_most_expr<'l>))] + #[kinds((Valueless))] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> Valueless<_> { + rhs + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + rhs + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + rhs + }, + } +} + +make_impls! { + #[kinds((Valueless))] + #[kinds((bool_at_most_expr<'r>))] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> Valueless<_> { + rhs.to_valueless() + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + rhs.to_valueless() + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + rhs.to_valueless() + }, + } +} + +macro_rules! impl_shift_binary_op_trait { + ( + [$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), + [][] (usize), + $(#[$meta:meta])* + $Trait:ident::$f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block $(,)? + ) => { + $(#[$meta])* + impl<$($LLifetimes)* $($LBounds)*> $Trait for $($L)* { + type Output = $Out<<::Type> as $Trait>::Output as ValueType>::Type>; + + #[track_caller] + fn $f($f_self, $f_rhs: usize) -> Self::Output $body } }; + ( + [$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), + [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), + $(#[$meta:meta])* + $Trait:ident::$f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block $(,)? + ) => { + $(#[$meta])* + impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*> $Trait<$($R)*> for $($L)* { + type Output = $Out<<::Type> as $Trait::Type>>>::Output as ValueType>::Type>; + + #[track_caller] + fn $f($f_self, $f_rhs: $($R)*) -> Self::Output $body + } + }; + ( + $LLifetimes:tt $LBounds:tt ($($L:tt)*), + $RLifetimes:tt $RBounds:tt ($($R:tt)*), + $(#[$meta:meta])* + $FirstTrait:ident::$first_f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block, + $($rest:tt)* + ) => { + impl_shift_binary_op_trait! { + $LLifetimes $LBounds ($($L)*), + $RLifetimes $RBounds ($($R)*), + $(#[$meta])* + $FirstTrait::$first_f($f_self, $f_rhs) -> $Out<_> $body + } + impl_shift_binary_op_trait! { + $LLifetimes $LBounds ($($L)*), + $RLifetimes $RBounds ($($R)*), + $(#[$meta])* + $($rest)* + } + }; +} + +make_impls! { + #[kinds((Expr>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> Expr<_> { + FixedShlU::new(Expr::as_dyn_int(self.to_expr()), rhs).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + FixedShrU::new(Expr::as_dyn_int(self.to_expr()), rhs).to_expr() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> Expr<_> { + FixedShlS::new(Expr::as_dyn_int(self.to_expr()), rhs).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + FixedShrS::new(Expr::as_dyn_int(self.to_expr()), rhs).to_expr() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_expr<'l, L>))] + #[kinds((Expr>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Expr<_> { + DynShlU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs)).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrU::new(self.to_expr(), Expr::as_dyn_int(rhs)).to_expr() + }, + } +} + +make_impls! { + #[kinds((sint_at_most_expr<'l, L>))] + #[kinds((Expr>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Expr<_> { + DynShlS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs)).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrS::new(self.to_expr(), Expr::as_dyn_int(rhs)).to_expr() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + #[kinds((uint_local_at_most_sim_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Expr<_> { + DynShlU::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrU::new(self, Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + #[kinds((uint_local_at_most_sim_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Expr<_> { + DynShlS::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrS::new(self, Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_sim_value<'l, L>))] + #[kinds((uint_sim_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_at_most_sim_value<'l, L>))] + #[kinds((uint_sim_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_sim_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_sim_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_sim_value<'l, L>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_sim_value<'l, L>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((sint_at_most_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((uint_value<'l, L>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shr(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((sint_value<'l, L>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shr(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((uint_at_most_expr<'l, L>))] + #[kinds((Valueless>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Valueless<_> { + self.to_valueless().shl(rhs) + }, + Shr::shr(self, rhs) -> Valueless<_> { + self.to_valueless().shr(rhs) + }, + } +} + +make_impls! { + #[kinds((sint_at_most_expr<'l, L>))] + #[kinds((Valueless>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Valueless<_> { + self.to_valueless().shl(rhs) + }, + Shr::shr(self, rhs) -> Valueless<_> { + self.to_valueless().shr(rhs) + }, + } +} + +make_impls! { + #[kinds((Valueless>))] + #[kinds((uint_local_at_most_expr<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Valueless<_> { + self.shl(rhs.to_valueless()) + }, + Shr::shr(self, rhs) -> Valueless<_> { + self.shr(rhs.to_valueless()) + }, + } +} + +make_impls! { + #[kinds((Valueless>))] + #[kinds((uint_local_at_most_expr<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Valueless<_> { + self.shl(rhs.to_valueless()) + }, + Shr::shr(self, rhs) -> Valueless<_> { + self.shr(rhs.to_valueless()) + }, + } +} + +macro_rules! impl_unary_op_trait { + ( + [$($Lifetimes:tt)*][$($Bounds:tt)*] ($($Ty:tt)*), + $(#[$meta:meta])* + $Trait:ident::$f:ident($f_self:ident) -> $Out:ident<_> $body:block $(,)? + ) => { + $(#[$meta])* + impl<$($Lifetimes)* $($Bounds)*> $Trait for $($Ty)* { + type Output = $Out<<::Type> as $Trait>::Output as ValueType>::Type>; + + fn $f($f_self) -> Self::Output $body + } + }; + ( + $Lifetimes:tt $Bounds:tt $Ty:tt, + $(#[$meta:meta])* + $FirstTrait:ident::$first_f:ident($f_self:ident) -> $Out:ident<_> $body:block, + $($rest:tt)* + ) => { + impl_unary_op_trait! { + $Lifetimes $Bounds $Ty, + $(#[$meta])* + $FirstTrait::$first_f($f_self) -> $Out<_> $body + } + impl_unary_op_trait! { + $Lifetimes $Bounds $Ty, + $(#[$meta])* + $($rest)* + } + }; +} + +make_impls! { + #[kinds((SimValue>))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::into_value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((&'a SimValue>))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + impl_unary_op_trait! { + Not::not(self) -> Expr<_> { + NotU::new(self).to_expr() + }, + } +} + +make_impls! { + #[kinds((SimValue>))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::into_value(self)).into_sim_value() + }, + StdNeg::neg(self) -> SimValue<_> { + StdNeg::neg(SimValue::into_value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((&'a SimValue>))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::value(self)).into_sim_value() + }, + StdNeg::neg(self) -> SimValue<_> { + StdNeg::neg(SimValue::value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + impl_unary_op_trait! { + Not::not(self) -> Expr<_> { + NotS::new(self).to_expr() + }, + StdNeg::neg(self) -> Expr<_> { + Neg::new(Expr::as_dyn_int(self)).to_expr() + }, + } +} + +make_impls! { + #[kinds((SimValue))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::into_value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((&'a SimValue))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(*SimValue::value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((Expr))] + impl_unary_op_trait! { + Not::not(self) -> Expr<_> { + NotB::new(self).to_expr() + }, + } } macro_rules! impl_get_target_none { @@ -107,9 +1819,16 @@ impl NotU { } } -impl ToExpr for NotU { +impl ValueType for NotU { type Type = UIntType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.arg.to_valueless().not().ty() + } +} + +impl ToExpr for NotU { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::NotU(NotU { @@ -117,7 +1836,7 @@ impl ToExpr for NotU { literal_bits: self.literal_bits, }) .intern(), - __ty: self.arg.__ty, + __ty: self.ty(), __flow: Flow::Source, } } @@ -131,19 +1850,6 @@ impl ToLiteralBits for NotU { impl_get_target_none!([Width: Size] NotU); -impl_unary_op_trait! { - #[generics(Width: Size)] - fn Not::not(arg: UIntType) -> UIntType { - NotU::new(arg).to_expr() - } -} - -forward_value_to_expr_unary_op_trait! { - #[generics(Width: Size)] - #[value(UIntValue)] - Not::not -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct NotS { arg: Expr>, @@ -164,9 +1870,16 @@ impl NotS { } } -impl ToExpr for NotS { +impl ValueType for NotS { type Type = UIntType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.arg.to_valueless().not().ty() + } +} + +impl ToExpr for NotS { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::NotS(NotS { @@ -174,7 +1887,7 @@ impl ToExpr for NotS { literal_bits: self.literal_bits, }) .intern(), - __ty: self.arg.__ty.as_same_width_uint(), + __ty: self.ty(), __flow: Flow::Source, } } @@ -188,19 +1901,6 @@ impl ToLiteralBits for NotS { impl_get_target_none!([Width: Size] NotS); -impl_unary_op_trait! { - #[generics(Width: Size)] - fn Not::not(arg: SIntType) -> UIntType { - NotS::new(arg).to_expr() - } -} - -forward_value_to_expr_unary_op_trait! { - #[generics(Width: Size)] - #[value(SIntValue)] - Not::not -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct NotB { arg: Expr, @@ -221,9 +1921,16 @@ impl NotB { } } -impl ToExpr for NotB { +impl ValueType for NotB { type Type = Bool; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + Bool + } +} + +impl ToExpr for NotB { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::NotB(NotB { @@ -231,7 +1938,7 @@ impl ToExpr for NotB { literal_bits: self.literal_bits, }) .intern(), - __ty: self.arg.__ty, + __ty: self.arg.ty(), __flow: Flow::Source, } } @@ -245,13 +1952,6 @@ impl ToLiteralBits for NotB { impl_get_target_none!([] NotB); -impl_unary_op_trait! { - #[generics()] - fn Not::not(arg: Bool) -> Bool { - NotB::new(arg).to_expr() - } -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct Neg { arg: Expr, @@ -270,22 +1970,21 @@ impl Neg { }); retval } - pub fn ty(self) -> SInt { - SInt::new_dyn( - Expr::ty(self.arg) - .width() - .checked_add(1) - .expect("width too big"), - ) - } pub fn arg(self) -> Expr { self.arg } } -impl ToExpr for Neg { +impl ValueType for Neg { type Type = SInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.arg.to_valueless().neg().ty() + } +} + +impl ToExpr for Neg { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::Neg(*self).intern(), @@ -303,64 +2002,6 @@ impl ToLiteralBits for Neg { impl_get_target_none!([] Neg); -impl_unary_op_trait! { - #[generics(Width: Size)] - fn StdNeg::neg(arg: SIntType) -> SInt { - Neg::new(Expr::as_dyn_int(arg)).to_expr() - } -} - -forward_value_to_expr_unary_op_trait! { - #[generics(Width: Size)] - #[value(SIntValue)] - StdNeg::neg -} - -macro_rules! impl_binary_op_trait { - ( - #[generics($($generics:tt)*)] - fn $Trait:ident::$method:ident($lhs:ident: $Lhs:ty, $rhs:ident: $Rhs:ty) -> $Output:ty { - $($body:tt)* - } - ) => { - impl< - Rhs: ToExpr, - $($generics)* - > $Trait for Expr<$Lhs> - { - type Output = Expr<$Output>; - - fn $method(self, rhs: Rhs) -> Self::Output { - let $lhs = self; - let $rhs = rhs.to_expr(); - $($body)* - } - } - }; -} - -macro_rules! forward_value_to_expr_binary_op_trait { - ( - #[generics($($generics:tt)*)] - #[lhs_value($LhsValue:ty)] - $Trait:ident::$method:ident - ) => { - impl< - Rhs, - $($generics)* - > $Trait for $LhsValue - where - Expr<<$LhsValue as ToExpr>::Type>: $Trait, - { - type Output = ::Type> as $Trait>::Output; - - fn $method(self, rhs: Rhs) -> Self::Output { - $Trait::$method(self.to_expr(), rhs) - } - } - }; -} - fn binary_op_literal_bits( result_ty: ResultTy, lhs: Expr, @@ -412,9 +2053,16 @@ macro_rules! binary_op_bitwise { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + $ty + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -423,17 +2071,6 @@ macro_rules! binary_op_bitwise { } } } - - /// intentionally only implemented for expressions, not rust's bool type, - /// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do - /// what is usually wanted. - impl $Trait for Expr { - type Output = Expr; - - fn $method(self, rhs: Expr) -> Expr { - $name::new(self, rhs).to_expr() - } - } }; ($name:ident, $ty:ident, $value:ident, $Trait:ident::$method:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -461,9 +2098,6 @@ macro_rules! binary_op_bitwise { pub fn rhs(self) -> Expr<$ty> { self.rhs } - pub fn ty(self) -> UInt { - UInt::new_dyn(Expr::ty(self.lhs).width().max(Expr::ty(self.rhs).width())) - } } impl ToLiteralBits for $name { @@ -474,9 +2108,16 @@ macro_rules! binary_op_bitwise { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = UInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().$method(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -485,19 +2126,6 @@ macro_rules! binary_op_bitwise { } } } - - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn $Trait::$method(lhs: $ty, rhs: $ty) -> UInt { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } - } - - forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value($value)] - $Trait::$method - } }; } @@ -538,15 +2166,6 @@ macro_rules! binary_op_add_sub { pub fn rhs(self) -> Expr<$ty> { self.rhs } - pub fn ty(self) -> $ty { - $ty::new_dyn( - Expr::ty(self.lhs) - .width() - .max(Expr::ty(self.rhs).width()) - .checked_add(1) - .expect("width too big"), - ) - } } impl ToLiteralBits for $name { @@ -557,9 +2176,16 @@ macro_rules! binary_op_add_sub { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + $Trait::$method(self.lhs.to_valueless(), self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -568,19 +2194,6 @@ macro_rules! binary_op_add_sub { } } } - - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn $Trait::$method(lhs: $ty, rhs: $ty) -> $ty { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } - } - - forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value($value)] - $Trait::$method - } }; } @@ -616,14 +2229,6 @@ macro_rules! binary_op_mul { pub fn rhs(self) -> Expr<$ty> { self.rhs } - pub fn ty(self) -> $ty { - $ty::new_dyn( - Expr::ty(self.lhs) - .width() - .checked_add(Expr::ty(self.rhs).width()) - .expect("width too big"), - ) - } } impl ToLiteralBits for $name { @@ -634,9 +2239,16 @@ macro_rules! binary_op_mul { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().mul(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -645,19 +2257,6 @@ macro_rules! binary_op_mul { } } } - - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Mul::mul(lhs: $ty, rhs: $ty) -> $ty { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } - } - - forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value($value)] - Mul::mul - } }; } @@ -689,9 +2288,6 @@ impl DivU { pub fn rhs(self) -> Expr { self.rhs } - pub fn ty(self) -> UIntType { - Expr::ty(self.lhs) - } } impl ToLiteralBits for DivU { @@ -702,9 +2298,16 @@ impl ToLiteralBits for DivU { impl_get_target_none!([LhsWidth: Size] DivU); -impl ToExpr for DivU { +impl ValueType for DivU { type Type = UIntType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().div(self.rhs.to_valueless()).ty() + } +} + +impl ToExpr for DivU { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::DivU(DivU { @@ -719,19 +2322,6 @@ impl ToExpr for DivU { } } -impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Div::div(lhs: UIntType, rhs: UIntType) -> UIntType { - DivU::new(lhs, Expr::as_dyn_int(rhs)).to_expr() - } -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(UIntValue)] - Div::div -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct DivS { lhs: Expr, @@ -757,14 +2347,6 @@ impl DivS { pub fn rhs(self) -> Expr { self.rhs } - pub fn ty(self) -> SInt { - SInt::new_dyn( - Expr::ty(self.lhs) - .width() - .checked_add(1) - .expect("width too big"), - ) - } } impl ToLiteralBits for DivS { @@ -775,9 +2357,16 @@ impl ToLiteralBits for DivS { impl_get_target_none!([] DivS); -impl ToExpr for DivS { +impl ValueType for DivS { type Type = SInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().div(self.rhs.to_valueless()).ty() + } +} + +impl ToExpr for DivS { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::DivS(*self).intern(), @@ -787,19 +2376,6 @@ impl ToExpr for DivS { } } -impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Div::div(lhs: SIntType, rhs: SIntType) -> SInt { - DivS::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(SIntValue)] - Div::div -} - macro_rules! binary_op_rem { ($name:ident, $ty:ident, $value:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -831,9 +2407,6 @@ macro_rules! binary_op_rem { pub fn rhs(self) -> Expr<$ty> { self.rhs } - pub fn ty(self) -> $ty { - $ty::new_dyn(Expr::ty(self.lhs).width().min(Expr::ty(self.rhs).width())) - } } impl ToLiteralBits for $name { @@ -844,9 +2417,16 @@ macro_rules! binary_op_rem { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().rem(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -855,19 +2435,6 @@ macro_rules! binary_op_rem { } } } - - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Rem::rem(lhs: $ty, rhs: $ty) -> $ty { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } - } - - forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value($value)] - Rem::rem - } }; } @@ -894,7 +2461,7 @@ impl BundleLiteral { for (&field, &field_value) in fields.iter().zip(field_values.iter()) { assert_eq!( field.ty, - Expr::ty(field_value), + field_value.ty(), "field's type doesn't match value's type: field name {:?}", field.name ); @@ -914,9 +2481,6 @@ impl BundleLiteral { .ok_or(NotALiteralExpr), } } - pub fn ty(self) -> T { - self.ty - } pub fn field_values(self) -> Interned<[Expr]> { self.field_values } @@ -930,9 +2494,16 @@ impl ToLiteralBits for BundleLiteral { impl_get_target_none!([T: BundleType] BundleLiteral); -impl ToExpr for BundleLiteral { +impl ValueType for BundleLiteral { type Type = T; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for BundleLiteral { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::BundleLiteral(BundleLiteral { @@ -968,7 +2539,7 @@ impl ArrayLiteral { for &element_value in element_values.iter() { assert_eq!( canonical_element_type, - Expr::ty(element_value), + element_value.ty(), "array's element type doesn't match element value's type", ); if let (Some(literal_bits), Ok(element_bits)) = @@ -997,12 +2568,6 @@ impl ArrayLiteral { pub fn is_empty(self) -> bool { self.element_values.is_empty() } - pub fn ty(self) -> ArrayType { - ArrayType::new( - self.element_type, - Len::from_usize(self.element_values.len()), - ) - } pub fn element_values(self) -> Interned<[Expr]> { self.element_values } @@ -1016,9 +2581,19 @@ impl ToLiteralBits for ArrayLiteral { impl_get_target_none!([Element: Type, Len: Size] ArrayLiteral); -impl ToExpr for ArrayLiteral { +impl ValueType for ArrayLiteral { type Type = ArrayType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + ArrayType::new( + self.element_type, + Len::from_usize(self.element_values.len()), + ) + } +} + +impl ToExpr for ArrayLiteral { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::ArrayLiteral(ArrayLiteral { @@ -1034,12 +2609,22 @@ impl ToExpr for ArrayLiteral { } } -impl, const N: usize> ToExpr for [T; N] +impl, const N: usize> ValueType for [T; N] where ConstUsize: KnownSize, { type Type = Array; + type ValueCategory = T::ValueCategory; + fn ty(&self) -> Self::Type { + StaticType::TYPE + } +} + +impl, const N: usize> ToExpr for [T; N] +where + ConstUsize: KnownSize, +{ fn to_expr(&self) -> Expr { ArrayLiteral::new( T::Type::TYPE, @@ -1049,9 +2634,16 @@ where } } +impl> ValueType for [T] { + type Type = Array; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + ArrayType::new_dyn(StaticType::TYPE, self.len()) + } +} + impl> ToExpr for [T] { - type Type = Array; - fn to_expr(&self) -> Expr { ArrayLiteral::new( T::Type::TYPE, @@ -1061,9 +2653,16 @@ impl> ToExpr for [T] { } } -impl> ToExpr for Vec { +impl> ValueType for Vec { type Type = Array; + type ValueCategory = T::ValueCategory; + fn ty(&self) -> Self::Type { + <[T]>::ty(self) + } +} + +impl> ToExpr for Vec { fn to_expr(&self) -> Expr { <[T]>::to_expr(self) } @@ -1090,7 +2689,7 @@ impl EnumLiteral { assert!(variant_index < variants.len()); assert_eq!( variants[variant_index].ty, - variant_value.map(Expr::ty), + variant_value.map(|v| v.ty()), "variant's type doesn't match value's type: variant name {:?}", variants[variant_index].name ); @@ -1130,9 +2729,6 @@ impl EnumLiteral { }; Self::new_by_index(ty, *variant_index, variant_value) } - pub fn ty(self) -> T { - self.ty - } pub fn variant_index(self) -> usize { self.variant_index } @@ -1152,9 +2748,16 @@ impl ToLiteralBits for EnumLiteral { impl_get_target_none!([T: EnumType] EnumLiteral); -impl ToExpr for EnumLiteral { +impl ValueType for EnumLiteral { type Type = T; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for EnumLiteral { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::EnumLiteral(EnumLiteral { @@ -1199,25 +2802,6 @@ macro_rules! impl_dyn_shl { pub fn rhs(self) -> Expr { self.rhs } - #[track_caller] - pub fn ty(self) -> $ty { - let Some(pow2_rhs_width) = Expr::ty(self.rhs) - .width() - .try_into() - .ok() - .and_then(|v| 2usize.checked_pow(v)) - else { - panic!( - "dynamic left-shift amount's bit-width is too big, try casting the shift \ - amount to a smaller bit-width before shifting" - ); - }; - $ty::new_dyn( - (pow2_rhs_width - 1) - .checked_add(Expr::ty(self.lhs).width()) - .expect("width too big"), - ) - } } impl ToLiteralBits for $name { @@ -1228,9 +2812,17 @@ macro_rules! impl_dyn_shl { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + #[track_caller] + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().shl(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { #[track_caller] fn to_expr(&self) -> Expr { Expr { @@ -1240,14 +2832,6 @@ macro_rules! impl_dyn_shl { } } } - - impl Shl>> for Expr<$ty> { - type Output = Expr<$ty>; - - fn shl(self, rhs: Expr>) -> Self::Output { - $name::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs)).to_expr() - } - } }; } @@ -1272,7 +2856,7 @@ macro_rules! impl_dyn_shr { literal_bits: Err(NotALiteralExpr), }; retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| { - Ok(lhs << rhs.to_usize().ok_or(NotALiteralExpr)?) + Ok(lhs >> rhs.to_usize().ok_or(NotALiteralExpr)?) }); retval } @@ -1282,10 +2866,6 @@ macro_rules! impl_dyn_shr { pub fn rhs(self) -> Expr { self.rhs } - #[track_caller] - pub fn ty(self) -> $ty { - Expr::ty(self.lhs) - } } impl ToLiteralBits for $name { @@ -1296,9 +2876,17 @@ macro_rules! impl_dyn_shr { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + #[track_caller] + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().shr(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { #[track_caller] fn to_expr(&self) -> Expr { Expr { @@ -1308,31 +2896,19 @@ macro_rules! impl_dyn_shr { literal_bits: self.literal_bits, }) .intern(), - __ty: Expr::ty(self.lhs), + __ty: self.ty(), __flow: Flow::Source, } } } - - impl Shr>> for Expr<$ty> { - type Output = Expr<$ty>; - - fn shr(self, rhs: Expr>) -> Self::Output { - $name::new(self, Expr::as_dyn_int(rhs)).to_expr() - } - } }; } impl_dyn_shr!(DynShrU, UIntType, UIntValue); impl_dyn_shr!(DynShrS, SIntType, SIntValue); -fn fixed_shr_width(lhs_width: usize, rhs: usize) -> Option { - Some(lhs_width.saturating_sub(rhs).max(1)) -} - macro_rules! binary_op_fixed_shift { - ($name:ident, $ty:ident, $value:ident, $width_fn:path, $Trait:ident::$method:ident) => { + ($name:ident, $ty:ident, $Trait:ident::$method:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct $name { lhs: Expr<$ty>, @@ -1361,11 +2937,6 @@ macro_rules! binary_op_fixed_shift { pub fn rhs(self) -> usize { self.rhs } - pub fn ty(self) -> $ty { - $ty::new_dyn( - $width_fn(Expr::ty(self.lhs).width(), self.rhs).expect("width too big"), - ) - } } impl ToLiteralBits for $name { @@ -1376,9 +2947,16 @@ macro_rules! binary_op_fixed_shift { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().$method(self.rhs).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -1387,98 +2965,26 @@ macro_rules! binary_op_fixed_shift { } } } - - impl $Trait for Expr<$ty> { - type Output = Expr<$ty>; - - fn $method(self, rhs: usize) -> Self::Output { - $name::new(Expr::as_dyn_int(self), rhs).to_expr() - } - } }; } -binary_op_fixed_shift!(FixedShlU, UIntType, UIntValue, usize::checked_add, Shl::shl); -binary_op_fixed_shift!(FixedShlS, SIntType, SIntValue, usize::checked_add, Shl::shl); -binary_op_fixed_shift!(FixedShrU, UIntType, UIntValue, fixed_shr_width, Shr::shr); -binary_op_fixed_shift!(FixedShrS, SIntType, SIntValue, fixed_shr_width, Shr::shr); - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(UIntValue)] - Shl::shl -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(SIntValue)] - Shl::shl -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(UIntValue)] - Shr::shr -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(SIntValue)] - Shr::shr -} - -pub trait ExprPartialEq: Type { - fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr; - fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr; -} - -pub trait ExprPartialOrd: ExprPartialEq { - fn cmp_lt(lhs: Expr, rhs: Expr) -> Expr; - fn cmp_le(lhs: Expr, rhs: Expr) -> Expr; - fn cmp_gt(lhs: Expr, rhs: Expr) -> Expr; - fn cmp_ge(lhs: Expr, rhs: Expr) -> Expr; -} - -impl HdlPartialEq for Lhs -where - Lhs::Type: ExprPartialEq, -{ - fn cmp_eq(self, rhs: Rhs) -> Expr { - ExprPartialEq::cmp_eq(self.to_expr(), rhs.to_expr()) - } - fn cmp_ne(self, rhs: Rhs) -> Expr { - ExprPartialEq::cmp_ne(self.to_expr(), rhs.to_expr()) - } -} - -impl HdlPartialOrd for Lhs -where - Lhs::Type: ExprPartialOrd, -{ - fn cmp_lt(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_lt(self.to_expr(), rhs.to_expr()) - } - fn cmp_le(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_le(self.to_expr(), rhs.to_expr()) - } - fn cmp_gt(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_gt(self.to_expr(), rhs.to_expr()) - } - fn cmp_ge(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_ge(self.to_expr(), rhs.to_expr()) - } -} +binary_op_fixed_shift!(FixedShlU, UInt, Shl::shl); +binary_op_fixed_shift!(FixedShlS, SInt, Shl::shl); +binary_op_fixed_shift!(FixedShrU, UInt, Shr::shr); +binary_op_fixed_shift!(FixedShrS, SInt, Shr::shr); macro_rules! impl_compare_op { ( $(#[width($LhsWidth:ident, $RhsWidth:ident)])? #[dyn_type($DynTy:ident)] #[to_dyn_type($lhs:ident => $dyn_lhs:expr, $rhs:ident => $dyn_rhs:expr)] + #[to_cmp_value($lhs_compare_value:ident => $lhs_compare_value_expr:expr, $rhs_compare_value:ident => $rhs_compare_value_expr:expr)] #[type($Lhs:ty, $Rhs:ty)] #[trait($Trait:ident)] $( struct $name:ident; - fn $method:ident(); + fn $value_method:ident(); + fn $expr_method:ident(); $CmpTrait:ident::$cmp_method:ident(); )* ) => { @@ -1515,9 +3021,16 @@ macro_rules! impl_compare_op { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = Bool; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + Bool + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -1528,7 +3041,15 @@ macro_rules! impl_compare_op { })* impl$(<$LhsWidth: Size, $RhsWidth: Size>)? $Trait<$Rhs> for $Lhs { - $(fn $method($lhs: Expr, $rhs: Expr<$Rhs>) -> Expr { + $(fn $value_method( + _lhs: Self, + $lhs_compare_value: Cow<'_, ::SimValue>, + _rhs: $Rhs, + $rhs_compare_value: Cow<'_, <$Rhs as Type>::SimValue>, + ) -> bool { + $CmpTrait::$cmp_method($lhs_compare_value_expr, $rhs_compare_value_expr) + })* + $(fn $expr_method($lhs: Expr, $rhs: Expr<$Rhs>) -> Expr { $name::new($dyn_lhs, $dyn_rhs).to_expr() })* } @@ -1538,79 +3059,192 @@ macro_rules! impl_compare_op { impl_compare_op! { #[dyn_type(Bool)] #[to_dyn_type(lhs => lhs, rhs => rhs)] + #[to_cmp_value(lhs_value => &*lhs_value, rhs_value => &*rhs_value)] #[type(Bool, Bool)] - #[trait(ExprPartialEq)] - struct CmpEqB; fn cmp_eq(); PartialEq::eq(); - struct CmpNeB; fn cmp_ne(); PartialEq::ne(); + #[trait(HdlPartialEqImpl)] + struct CmpEqB; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); + struct CmpNeB; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); } impl_compare_op! { #[dyn_type(Bool)] #[to_dyn_type(lhs => lhs, rhs => rhs)] + #[to_cmp_value(lhs_value => &*lhs_value, rhs_value => &*rhs_value)] #[type(Bool, Bool)] - #[trait(ExprPartialOrd)] - struct CmpLtB; fn cmp_lt(); PartialOrd::lt(); - struct CmpLeB; fn cmp_le(); PartialOrd::le(); - struct CmpGtB; fn cmp_gt(); PartialOrd::gt(); - struct CmpGeB; fn cmp_ge(); PartialOrd::ge(); + #[trait(HdlPartialOrdImpl)] + struct CmpLtB; fn cmp_value_lt(); fn cmp_expr_lt(); PartialOrd::lt(); + struct CmpLeB; fn cmp_value_le(); fn cmp_expr_le(); PartialOrd::le(); + struct CmpGtB; fn cmp_value_gt(); fn cmp_expr_gt(); PartialOrd::gt(); + struct CmpGeB; fn cmp_value_ge(); fn cmp_expr_ge(); PartialOrd::ge(); } impl_compare_op! { #[width(LhsWidth, RhsWidth)] #[dyn_type(UInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(UIntType, UIntType)] - #[trait(ExprPartialEq)] - struct CmpEqU; fn cmp_eq(); PartialEq::eq(); - struct CmpNeU; fn cmp_ne(); PartialEq::ne(); + #[trait(HdlPartialEqImpl)] + struct CmpEqU; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); + struct CmpNeU; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); } impl_compare_op! { #[width(LhsWidth, RhsWidth)] #[dyn_type(UInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(UIntType, UIntType)] - #[trait(ExprPartialOrd)] - struct CmpLtU; fn cmp_lt(); PartialOrd::lt(); - struct CmpLeU; fn cmp_le(); PartialOrd::le(); - struct CmpGtU; fn cmp_gt(); PartialOrd::gt(); - struct CmpGeU; fn cmp_ge(); PartialOrd::ge(); + #[trait(HdlPartialOrdImpl)] + struct CmpLtU; fn cmp_value_lt(); fn cmp_expr_lt(); PartialOrd::lt(); + struct CmpLeU; fn cmp_value_le(); fn cmp_expr_le(); PartialOrd::le(); + struct CmpGtU; fn cmp_value_gt(); fn cmp_expr_gt(); PartialOrd::gt(); + struct CmpGeU; fn cmp_value_ge(); fn cmp_expr_ge(); PartialOrd::ge(); } impl_compare_op! { #[width(LhsWidth, RhsWidth)] #[dyn_type(SInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(SIntType, SIntType)] - #[trait(ExprPartialEq)] - struct CmpEqS; fn cmp_eq(); PartialEq::eq(); - struct CmpNeS; fn cmp_ne(); PartialEq::ne(); + #[trait(HdlPartialEqImpl)] + struct CmpEqS; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); + struct CmpNeS; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); } impl_compare_op! { #[width(LhsWidth, RhsWidth)] #[dyn_type(SInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(SIntType, SIntType)] - #[trait(ExprPartialOrd)] - struct CmpLtS; fn cmp_lt(); PartialOrd::lt(); - struct CmpLeS; fn cmp_le(); PartialOrd::le(); - struct CmpGtS; fn cmp_gt(); PartialOrd::gt(); - struct CmpGeS; fn cmp_ge(); PartialOrd::ge(); + #[trait(HdlPartialOrdImpl)] + struct CmpLtS; fn cmp_value_lt(); fn cmp_expr_lt(); PartialOrd::lt(); + struct CmpLeS; fn cmp_value_le(); fn cmp_expr_le(); PartialOrd::le(); + struct CmpGtS; fn cmp_value_gt(); fn cmp_expr_gt(); PartialOrd::gt(); + struct CmpGeS; fn cmp_value_ge(); fn cmp_expr_ge(); PartialOrd::ge(); } -pub trait ExprCastTo: Type { - fn cast_to(src: Expr, to_type: ToType) -> Expr; +macro_rules! impl_compare_forwards_to_bool { + ($ty:ident) => { + impl HdlPartialEqImpl for $ty { + #[track_caller] + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, Self::SimValue>, + ) -> bool { + *lhs_value == *rhs_value + } + + #[track_caller] + fn cmp_expr_eq(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_eq(rhs.cast_to(Bool)) + } + + #[track_caller] + fn cmp_expr_ne(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_ne(rhs.cast_to(Bool)) + } + } + + impl HdlPartialOrdImpl for $ty { + #[track_caller] + fn cmp_value_lt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + PartialOrd::lt(&*lhs_value, &*rhs_value) + } + + #[track_caller] + fn cmp_value_le( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + PartialOrd::le(&*lhs_value, &*rhs_value) + } + + #[track_caller] + fn cmp_value_gt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + PartialOrd::gt(&*lhs_value, &*rhs_value) + } + + #[track_caller] + fn cmp_value_ge( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + PartialOrd::ge(&*lhs_value, &*rhs_value) + } + + #[track_caller] + fn cmp_expr_lt(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_lt(rhs.cast_to(Bool)) + } + + #[track_caller] + fn cmp_expr_le(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_le(rhs.cast_to(Bool)) + } + + #[track_caller] + fn cmp_expr_gt(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_gt(rhs.cast_to(Bool)) + } + + #[track_caller] + fn cmp_expr_ge(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_ge(rhs.cast_to(Bool)) + } + } + }; } -impl ExprCastTo for Bool { - fn cast_to(src: Expr, _to_type: Bool) -> Expr { - src +impl_compare_forwards_to_bool!(Clock); +impl_compare_forwards_to_bool!(Reset); +impl_compare_forwards_to_bool!(SyncReset); +impl_compare_forwards_to_bool!(AsyncReset); + +impl CastToImpl for Bool { + type ValueOutput = bool; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: Bool, + ) -> Self::ValueOutput { + *value + } + + fn cast_sim_value_to(value: Cow<'_, SimValue>, _to_type: Bool) -> SimValue { + value.into_owned() + } + + fn cast_expr_to(value: Expr, _to_type: Bool) -> Expr { + value + } + + fn cast_valueless_to(value: Valueless, _to_type: Bool) -> Valueless { + value } } macro_rules! impl_cast_int_op { - ($name:ident, $from:ident, $to:ident) => { + ($name:ident, $from:ident, $to:ident, $to_value:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct $name { arg: Expr<$from>, @@ -1633,9 +3267,6 @@ macro_rules! impl_cast_int_op { pub fn arg(self) -> Expr<$from> { self.arg } - pub fn ty(self) -> $to { - self.ty - } } impl ToLiteralBits for $name { @@ -1646,9 +3277,16 @@ macro_rules! impl_cast_int_op { impl_get_target_none!([ToWidth: Size] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $to; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name($name { @@ -1663,55 +3301,64 @@ macro_rules! impl_cast_int_op { } } - impl ExprCastTo<$to> for $from { - fn cast_to(src: Expr, to_type: $to) -> Expr<$to> { - $name::new(Expr::as_dyn_int(src), to_type).to_expr() + impl CastToImpl<$to> for $from { + type ValueOutput = $to_value; + + fn cast_value_to(_this: Self, value: Cow<'_, Self::SimValue>, to_type: $to) -> Self::ValueOutput { + to_type.from_int_wrapping(value.to_bigint()) + } + + fn cast_expr_to(value: Expr, to_type: $to) -> Expr<$to> { + $name::new(Expr::as_dyn_int(value), to_type).to_expr() } } }; } -impl_cast_int_op!(CastUIntToUInt, UIntType, UIntType); -impl_cast_int_op!(CastUIntToSInt, UIntType, SIntType); -impl_cast_int_op!(CastSIntToUInt, SIntType, UIntType); -impl_cast_int_op!(CastSIntToSInt, SIntType, SIntType); +impl_cast_int_op!(CastUIntToUInt, UIntType, UIntType, UIntValue); +impl_cast_int_op!(CastUIntToSInt, UIntType, SIntType, SIntValue); +impl_cast_int_op!(CastSIntToUInt, SIntType, UIntType, UIntValue); +impl_cast_int_op!(CastSIntToSInt, SIntType, SIntType, SIntValue); macro_rules! impl_cast_bit_op { - ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[trait] $Trait:ident::$trait_fn:ident $(,)?) => { - impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to); + ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[to_value = $to_value_expr:expr] $to_value:ty, #[trait] $Trait:ident::$trait_fn:ident $(,)?) => { + impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to, #[to_value = $to_value_expr] $to_value); impl $Trait for Expr<$from> { - fn $trait_fn(&self) -> Expr<$to> { + type Output = Expr<$to>; + + fn $trait_fn(&self) -> Self::Output { self.cast_to_static() } } $(impl $Trait for Expr<$from_dyn> { - fn $trait_fn(&self) -> Expr<$to> { + type Output = Expr<$to>; + + fn $trait_fn(&self) -> Self::Output { self.cast_to_static() } })? }; - ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[dyn] $to_dyn:ty $(,)?) => { - impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to); + ($name:ident, $from:ty, $to:ty, #[to_value = $to_value_expr:expr] $to_value:ty, #[dyn] $to_dyn:ty $(,)?) => { + impl_cast_bit_op!($name, $from, $to, #[to_value = $to_value_expr] $to_value); - impl ExprCastTo<$to_dyn> for $from { - fn cast_to(src: Expr, to_type: $to_dyn) -> Expr<$to_dyn> { + impl CastToImpl<$to_dyn> for $from { + type ValueOutput = <$to_dyn as BoolOrIntType>::Value; + + fn cast_value_to(this: Self, value: Cow<'_, Self::SimValue>, to_type: $to_dyn) -> Self::ValueOutput { + >::cast_value_to(this, value, StaticType::TYPE).cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: $to_dyn) -> Expr<$to_dyn> { assert!(to_type.width() == 1); Expr::as_dyn_int( - $name::new(src).to_expr(), + $name::new(value).to_expr(), ) } } - - $(impl ExprCastTo<$to_dyn> for $from_dyn { - fn cast_to(src: Expr, to_type: $to_dyn) -> Expr<$to_dyn> { - assert!(to_type.width() == 1); - Expr::as_dyn_int($name::new(Expr::from_dyn_int(src)).to_expr()) - } - })? }; - ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty $(,)?) => { + ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[to_value = $to_value_expr:expr] $to_value:ty $(,)?) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct $name { arg: Expr<$from>, @@ -1738,9 +3385,16 @@ macro_rules! impl_cast_bit_op { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $to; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + Self::Type::TYPE + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -1750,50 +3404,88 @@ macro_rules! impl_cast_bit_op { } } - impl ExprCastTo<$to> for $from { - fn cast_to(src: Expr, _to_type: $to) -> Expr<$to> { - $name::new(src).to_expr() + impl CastToImpl<$to> for $from { + type ValueOutput = $to_value; + + fn cast_value_to(this: Self, value: Cow<'_, Self::SimValue>, to_type: $to) -> Self::ValueOutput { + ($to_value_expr)(this, value, to_type) + } + + fn cast_expr_to(value: Expr, _to_type: $to) -> Expr<$to> { + $name::new(value).to_expr() } } - $(impl ExprCastTo<$to> for $from_dyn { - fn cast_to(src: Expr, _to_type: $to) -> Expr<$to> { - $name::new(Expr::<$from>::from_dyn_int(src)).to_expr() + $(impl CastToImpl<$to> for $from_dyn { + type ValueOutput = $to_value; + + fn cast_value_to(this: Self, value: Cow<'_, Self::SimValue>, to_type: $to) -> Self::ValueOutput { + <$from_dyn as CastToImpl<$from>>::cast_value_to(this, value, StaticType::TYPE).cast_to(to_type) + } + + fn cast_expr_to(value: Expr, _to_type: $to) -> Expr<$to> { + $name::new(Expr::<$from>::from_dyn_int(value)).to_expr() } })? }; } -impl_cast_bit_op!(CastBoolToUInt, Bool, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastBoolToSInt, Bool, SInt<1>, #[dyn] SInt); -impl_cast_bit_op!(CastUIntToBool, UInt<1>, #[dyn] UInt, Bool); -impl_cast_bit_op!(CastSIntToBool, SInt<1>, #[dyn] SInt, Bool); -impl_cast_bit_op!(CastBoolToSyncReset, Bool, SyncReset, #[trait] ToSyncReset::to_sync_reset); -impl_cast_bit_op!(CastUIntToSyncReset, UInt<1>, #[dyn] UInt, SyncReset, #[trait] ToSyncReset::to_sync_reset); -impl_cast_bit_op!(CastSIntToSyncReset, SInt<1>, #[dyn] SInt, SyncReset, #[trait] ToSyncReset::to_sync_reset); -impl_cast_bit_op!(CastBoolToAsyncReset, Bool, AsyncReset, #[trait] ToAsyncReset::to_async_reset); -impl_cast_bit_op!(CastUIntToAsyncReset, UInt<1>, #[dyn] UInt, AsyncReset, #[trait] ToAsyncReset::to_async_reset); -impl_cast_bit_op!(CastSIntToAsyncReset, SInt<1>, #[dyn] SInt, AsyncReset, #[trait] ToAsyncReset::to_async_reset); -impl_cast_bit_op!(CastSyncResetToBool, SyncReset, Bool); -impl_cast_bit_op!(CastSyncResetToUInt, SyncReset, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastSyncResetToSInt, SyncReset, SInt<1>, #[dyn] SInt); -impl_cast_bit_op!(CastSyncResetToReset, SyncReset, Reset); -impl_cast_bit_op!(CastAsyncResetToBool, AsyncReset, Bool); -impl_cast_bit_op!(CastAsyncResetToUInt, AsyncReset, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastAsyncResetToSInt, AsyncReset, SInt<1>, #[dyn] SInt); -impl_cast_bit_op!(CastAsyncResetToReset, AsyncReset, Reset); -impl_cast_bit_op!(CastResetToBool, Reset, Bool); -impl_cast_bit_op!(CastResetToUInt, Reset, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastResetToSInt, Reset, SInt<1>, #[dyn] SInt); -impl_cast_bit_op!(CastBoolToClock, Bool, Clock, #[trait] ToClock::to_clock); -impl_cast_bit_op!(CastUIntToClock, UInt<1>, #[dyn] UInt, Clock, #[trait] ToClock::to_clock); -impl_cast_bit_op!(CastSIntToClock, SInt<1>, #[dyn] SInt, Clock, #[trait] ToClock::to_clock); -impl_cast_bit_op!(CastClockToBool, Clock, Bool); -impl_cast_bit_op!(CastClockToUInt, Clock, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastClockToSInt, Clock, SInt<1>, #[dyn] SInt); +impl_cast_bit_op!(CastBoolToUInt, Bool, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastBoolToSInt, Bool, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); +impl_cast_bit_op!(CastUIntToBool, UInt<1>, #[dyn] UInt, Bool, #[to_value = |_, v: Cow<'_, UIntValue<_>>, _| v.bits()[0]] bool); +impl_cast_bit_op!(CastSIntToBool, SInt<1>, #[dyn] SInt, Bool, #[to_value = |_, v: Cow<'_, SIntValue<_>>, _| v.bits()[0]] bool); +impl_cast_bit_op!(CastBoolToSyncReset, Bool, SyncReset, #[to_value = |_, v: Cow<'_, bool>, _| v.to_sync_reset()] SimValue, #[trait] ToSyncReset::to_sync_reset); +impl_cast_bit_op!(CastUIntToSyncReset, UInt<1>, #[dyn] UInt, SyncReset, #[to_value = |_, v: Cow<'_, UIntValue<_>>, _| v.bits()[0].to_sync_reset()] SimValue, #[trait] ToSyncReset::to_sync_reset); +impl_cast_bit_op!(CastSIntToSyncReset, SInt<1>, #[dyn] SInt, SyncReset, #[to_value = |_, v: Cow<'_, SIntValue<_>>, _| v.bits()[0].to_sync_reset()] SimValue, #[trait] ToSyncReset::to_sync_reset); +impl_cast_bit_op!(CastBoolToAsyncReset, Bool, AsyncReset, #[to_value = |_, v: Cow<'_, bool>, _| v.to_async_reset()] SimValue, #[trait] ToAsyncReset::to_async_reset); +impl_cast_bit_op!(CastUIntToAsyncReset, UInt<1>, #[dyn] UInt, AsyncReset, #[to_value = |_, v: Cow<'_, UIntValue<_>>, _| v.bits()[0].to_async_reset()] SimValue, #[trait] ToAsyncReset::to_async_reset); +impl_cast_bit_op!(CastSIntToAsyncReset, SInt<1>, #[dyn] SInt, AsyncReset, #[to_value = |_, v: Cow<'_, SIntValue<_>>, _| v.bits()[0].to_async_reset()] SimValue, #[trait] ToAsyncReset::to_async_reset); +impl_cast_bit_op!( + CastSyncResetToBool, + SyncReset, + Bool, + #[to_value = |_, v: Cow<'_, bool>, _| *v] + bool +); +impl_cast_bit_op!(CastSyncResetToUInt, SyncReset, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastSyncResetToSInt, SyncReset, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); +impl_cast_bit_op!(CastSyncResetToReset, SyncReset, Reset, #[to_value = |_, v: Cow<'_, bool>, t| SimValue::from_value(t, *v)] SimValue); +impl_cast_bit_op!( + CastAsyncResetToBool, + AsyncReset, + Bool, + #[to_value = |_, v: Cow<'_, bool>, _| *v] + bool +); +impl_cast_bit_op!(CastAsyncResetToUInt, AsyncReset, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastAsyncResetToSInt, AsyncReset, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); +impl_cast_bit_op!(CastAsyncResetToReset, AsyncReset, Reset, #[to_value = |_, v: Cow<'_, bool>, t| SimValue::from_value(t, *v)] SimValue); +impl_cast_bit_op!( + CastResetToBool, + Reset, + Bool, + #[to_value = |_, v: Cow<'_, bool>, _| *v] + bool +); +impl_cast_bit_op!(CastResetToUInt, Reset, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastResetToSInt, Reset, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); +impl_cast_bit_op!(CastBoolToClock, Bool, Clock, #[to_value = |_, v: Cow<'_, bool>, _| v.to_clock()] SimValue, #[trait] ToClock::to_clock); +impl_cast_bit_op!(CastUIntToClock, UInt<1>, #[dyn] UInt, Clock, #[to_value = |_, v: Cow<'_, UIntValue<_>>, _| v.bits()[0].to_clock()] SimValue, #[trait] ToClock::to_clock); +impl_cast_bit_op!(CastSIntToClock, SInt<1>, #[dyn] SInt, Clock, #[to_value = |_, v: Cow<'_, SIntValue<_>>, _| v.bits()[0].to_clock()] SimValue, #[trait] ToClock::to_clock); +impl_cast_bit_op!( + CastClockToBool, + Clock, + Bool, + #[to_value = |_, v: Cow<'_, bool>, _| *v] + bool +); +impl_cast_bit_op!(CastClockToUInt, Clock, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastClockToSInt, Clock, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); impl ToReset for Expr { - fn to_reset(&self) -> Expr { + type Output = Expr; + + fn to_reset(&self) -> Self::Output { struct Dispatch; impl ResetTypeDispatch for Dispatch { type Input = Expr; @@ -1815,101 +3507,279 @@ impl ToReset for Expr { } } -impl ExprCastTo for AsyncReset { - fn cast_to(src: Expr, _to_type: AsyncReset) -> Expr { - src +impl ToReset for SimValue { + type Output = Expr; + + fn to_reset(&self) -> Self::Output { + (*self).to_expr().to_sync_reset().to_reset() } } -impl ExprCastTo for AsyncReset { - fn cast_to(src: Expr, to_type: SyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl ToReset for SimValue { + type Output = Expr; + + fn to_reset(&self) -> Self::Output { + (*self).to_expr().to_async_reset().to_reset() } } -impl ExprCastTo for AsyncReset { - fn cast_to(src: Expr, to_type: Clock) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for AsyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: AsyncReset, + ) -> Self::ValueOutput { + value.to_async_reset() + } + + fn cast_expr_to(value: Expr, _to_type: AsyncReset) -> Expr { + value } } -impl ExprCastTo for SyncReset { - fn cast_to(src: Expr, to_type: AsyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for AsyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: SyncReset, + ) -> Self::ValueOutput { + value.to_sync_reset() + } + + fn cast_expr_to(value: Expr, to_type: SyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for SyncReset { - fn cast_to(src: Expr, _to_type: SyncReset) -> Expr { - src +impl CastToImpl for AsyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: Clock, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: Clock) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for SyncReset { - fn cast_to(src: Expr, to_type: Clock) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for SyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: AsyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: AsyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Reset { - fn cast_to(src: Expr, to_type: AsyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for SyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: SyncReset, + ) -> Self::ValueOutput { + value.to_sync_reset() + } + + fn cast_expr_to(value: Expr, _to_type: SyncReset) -> Expr { + value } } -impl ExprCastTo for Reset { - fn cast_to(src: Expr, to_type: SyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for SyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: Clock, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: Clock) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Reset { - fn cast_to(src: Expr, _to_type: Reset) -> Expr { - src +impl CastToImpl for Reset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: AsyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: AsyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Reset { - fn cast_to(src: Expr, to_type: Clock) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for Reset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: SyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: SyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Clock { - fn cast_to(src: Expr, to_type: AsyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for Reset { + type ValueOutput = SimValue; + + #[track_caller] + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: Reset, + ) -> Self::ValueOutput { + SimValue::from_value(to_type, *value) + } + + #[track_caller] + fn cast_expr_to(value: Expr, _to_type: Reset) -> Expr { + value } } -impl ExprCastTo for Clock { - fn cast_to(src: Expr, to_type: SyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for Reset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: Clock, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: Clock) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Clock { - fn cast_to(src: Expr, _to_type: Clock) -> Expr { - src +impl CastToImpl for Clock { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: AsyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: AsyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo<()> for PhantomConst { - fn cast_to(src: Expr, to_type: ()) -> Expr<()> { - src.cast_to_bits().cast_bits_to(to_type) +impl CastToImpl for Clock { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: SyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: SyncReset) -> Expr { + value.cast_to(Bool).cast_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 CastToImpl for Clock { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: Clock, + ) -> Self::ValueOutput { + value.to_clock() + } + + fn cast_expr_to(value: Expr, _to_type: Clock) -> Expr { + value } } -impl ExprCastTo> +impl CastToImpl<()> for PhantomConst { + type ValueOutput = (); + + fn cast_value_to( + _this: Self, + _value: Cow<'_, Self::SimValue>, + _to_type: (), + ) -> Self::ValueOutput { + () + } + + fn cast_expr_to(value: Expr, to_type: ()) -> Expr<()> { + value.cast_to_bits().cast_bits_to(to_type) + } +} + +impl CastToImpl> for () { + type ValueOutput = PhantomConst; + + fn cast_value_to( + _this: Self, + _value: Cow<'_, Self::SimValue>, + to_type: PhantomConst, + ) -> Self::ValueOutput { + to_type + } + + fn cast_expr_to(value: Expr, to_type: PhantomConst) -> Expr> { + value.cast_to_bits().cast_bits_to(to_type) + } +} + +impl CastToImpl> for PhantomConst { - fn cast_to(src: Expr, to_type: PhantomConst) -> Expr> { - src.cast_to_bits().cast_bits_to(to_type) + type ValueOutput = PhantomConst; + + fn cast_value_to( + _this: Self, + _value: Cow<'_, Self::SimValue>, + to_type: PhantomConst, + ) -> Self::ValueOutput { + to_type + } + + fn cast_expr_to(value: Expr, to_type: PhantomConst) -> Expr> { + value.cast_to_bits().cast_bits_to(to_type) } } @@ -1934,10 +3804,10 @@ impl fmt::Debug for FieldAccess { impl FieldAccess { #[track_caller] pub fn new_by_index(base: Expr, field_index: usize) -> Self { - let field = Expr::ty(base).fields()[field_index]; + let field = base.ty().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].bit_width..][..field.ty.bit_width()] + bits[base.ty().field_offsets()[field_index].bit_width..][..field.ty.bit_width()] .intern() }); let target = base.target().map(|base| { @@ -1956,7 +3826,7 @@ impl FieldAccess { } #[track_caller] pub fn new_by_name(base: Expr, name: Interned) -> Self { - let base_ty = Expr::ty(base); + let base_ty = base.ty(); let Some(field_index) = base_ty.name_indexes().get(&name) else { panic!("unknown field {name:?}: in {base_ty:?}"); }; @@ -1991,9 +3861,16 @@ impl ToLiteralBits for FieldAccess { } } -impl ToExpr for FieldAccess { +impl ValueType for FieldAccess { type Type = FieldType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.field_type + } +} + +impl ToExpr for FieldAccess { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::FieldAccess(FieldAccess { @@ -2023,10 +3900,10 @@ pub struct VariantAccess { impl VariantAccess { #[track_caller] pub fn new_by_index(base: Expr, variant_index: usize) -> Self { - let variant = Expr::ty(base).variants()[variant_index]; + let variant = base.ty().variants()[variant_index]; let variant_type = variant.ty.map(VariantType::from_canonical); let literal_bits = base.to_literal_bits().and_then(|bits| { - let discriminant_bit_width = Expr::ty(base).discriminant_bit_width(); + let discriminant_bit_width = base.ty().discriminant_bit_width(); if bits[..discriminant_bit_width] != [variant_index].view_bits::()[..discriminant_bit_width] { @@ -2047,7 +3924,7 @@ impl VariantAccess { } #[track_caller] pub fn new_by_name(base: Expr, name: Interned) -> Self { - let base_ty = Expr::ty(base); + let base_ty = base.ty(); let Some(variant_index) = base_ty.name_indexes().get(&name) else { panic!("unknown variant {name:?}: in {base_ty:?}"); }; @@ -2078,9 +3955,17 @@ impl ToLiteralBits for VariantAccess { impl_get_target_none!([VariantType: Type] VariantAccess); -impl ToExpr for VariantAccess { +impl ValueType for VariantAccess { type Type = VariantType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.variant_type + .unwrap_or_else(|| VariantType::from_canonical(().canonical())) + } +} + +impl ToExpr for VariantAccess { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::VariantAccess(VariantAccess { @@ -2091,9 +3976,7 @@ impl ToExpr for VariantAccess { literal_bits: self.literal_bits, }) .intern(), - __ty: self - .variant_type - .unwrap_or_else(|| VariantType::from_canonical(().canonical())), + __ty: self.ty(), __flow: Flow::Source, } } @@ -2111,7 +3994,7 @@ pub struct ArrayIndex { impl ArrayIndex { #[track_caller] pub fn new(base: Expr, element_index: usize) -> Self { - let base_ty = Expr::ty(base); + let base_ty = base.ty(); assert!(element_index < base_ty.len()); let element_type = ElementType::from_canonical(base_ty.element()); let literal_bits = base.to_literal_bits().map(|bits| { @@ -2159,15 +4042,22 @@ impl GetTarget for ArrayIndex { } } -impl ToExpr for ArrayIndex { +impl ValueType for ArrayIndex { type Type = ElementType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.element_type + } +} + +impl ToExpr for ArrayIndex { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::ArrayIndex(ArrayIndex { base: self.base, element_index: self.element_index, - element_type: Expr::ty(self.base).element(), + element_type: self.base.ty().element(), literal_bits: self.literal_bits, target: self.target, }) @@ -2203,7 +4093,7 @@ pub struct DynArrayIndex { impl DynArrayIndex { #[track_caller] pub fn new(base: Expr, element_index: Expr) -> Self { - let base_ty = Expr::ty(base); + let base_ty = base.ty(); let element_type = ElementType::from_canonical(base_ty.element()); let literal_bits = base .to_literal_bits() @@ -2255,15 +4145,22 @@ impl GetTarget for DynArrayIndex { } } -impl ToExpr for DynArrayIndex { +impl ValueType for DynArrayIndex { type Type = ElementType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.element_type + } +} + +impl ToExpr for DynArrayIndex { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::DynArrayIndex(DynArrayIndex { base: self.base, element_index: self.element_index, - element_type: Expr::ty(self.base).element(), + element_type: self.base.ty().element(), literal_bits: self.literal_bits, target: self.target, }) @@ -2356,7 +4253,7 @@ macro_rules! impl_int_slice { impl $name { pub fn new(base: Expr<$ty>, range: Range) -> Self { assert!( - range.start <= range.end && range.end <= Expr::ty(base).width(), + range.start <= range.end && range.end <= base.ty().width(), "invalid range" ); let literal_bits = base @@ -2385,37 +4282,63 @@ macro_rules! impl_int_slice { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = UInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + UInt::new_dyn(self.range().len()) + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), - __ty: UInt::new_dyn(self.range().len()), + __ty: self.ty(), __flow: Flow::Source, } } } + impl> std::ops::Index for Valueless<$ty> { + type Output = Valueless; + + #[track_caller] + fn index(&self, index: I) -> &Self::Output { + let range = self.ty().slice_index_to_range(index); + Interned::into_inner(Valueless::new(UInt::new_dyn(range.len())).intern_sized()) + } + } + impl> ExprIndex for $ty { type Output = UInt; #[track_caller] fn expr_index(this: &Expr, index: I) -> &Expr { let base = Expr::as_dyn_int(*this); - let base_ty = Expr::ty(base); - let range = base_ty.slice_index_to_range(index); + let range = base.ty().slice_index_to_range(index); Interned::into_inner($name::new(base, range).to_expr().intern_sized()) } } + impl std::ops::Index for Valueless<$ty> { + type Output = Valueless; + + #[track_caller] + fn index(&self, index: usize) -> &Self::Output { + assert!(index < self.ty().width()); + &const { Valueless::new(Bool) } + } + } + impl ExprIndex for $ty { type Output = Bool; #[track_caller] fn expr_index(this: &Expr, index: usize) -> &Expr { let base = Expr::as_dyn_int(*this); - let base_ty = Expr::ty(base); + let base_ty = base.ty(); assert!(index < base_ty.width()); Interned::into_inner( $name::new(base, index..(index + 1)) @@ -2457,9 +4380,16 @@ macro_rules! impl_reduce_bits { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = UInt<1>; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + StaticType::TYPE + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -2480,126 +4410,99 @@ impl_reduce_bits!(ReduceBitXorU, UInt, |bits| (bits.count_ones() % 2 != 0) impl_reduce_bits!(ReduceBitXorS, SInt, |bits| (bits.count_ones() % 2 != 0) .to_literal_bits()); -impl ReduceBits for Expr> { - type UIntOutput = Expr>; - type BoolOutput = Expr; - - fn reduce_bitand(self) -> Self::UIntOutput { - ReduceBitAndU::new(Expr::as_dyn_int(self)).to_expr() +impl ReduceBitsImpl for UIntType { + fn value_reduce_bitand( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + value.bits().all().into() } - fn reduce_bitor(self) -> Self::UIntOutput { - ReduceBitOrU::new(Expr::as_dyn_int(self)).to_expr() + fn value_reduce_bitor(_this: Self, value: Cow<'_, Self::SimValue>) -> UIntValue> { + value.bits().any().into() } - fn reduce_bitxor(self) -> Self::UIntOutput { - ReduceBitXorU::new(Expr::as_dyn_int(self)).to_expr() + fn value_reduce_bitxor( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + (value.bits().count_ones() % 2 != 0).into() } - fn any_one_bits(self) -> Self::BoolOutput { - self.reduce_bitor().cast_to_static() + fn expr_reduce_bitand(this: Expr) -> Expr> { + ReduceBitAndU::new(Expr::as_dyn_int(this)).to_expr() } - fn any_zero_bits(self) -> Self::BoolOutput { - (!self.reduce_bitand()).cast_to_static() + fn expr_reduce_bitor(this: Expr) -> Expr> { + ReduceBitOrU::new(Expr::as_dyn_int(this)).to_expr() } - fn all_one_bits(self) -> Self::BoolOutput { - self.reduce_bitand().cast_to_static() - } - - fn all_zero_bits(self) -> Self::BoolOutput { - (!self.reduce_bitor()).cast_to_static() - } - - fn parity_odd(self) -> Self::BoolOutput { - self.reduce_bitxor().cast_to_static() - } - - fn parity_even(self) -> Self::BoolOutput { - (!self.reduce_bitxor()).cast_to_static() + fn expr_reduce_bitxor(this: Expr) -> Expr> { + ReduceBitXorU::new(Expr::as_dyn_int(this)).to_expr() } } -impl ReduceBits for Expr> { - type UIntOutput = Expr>; - type BoolOutput = Expr; - - fn reduce_bitand(self) -> Self::UIntOutput { - ReduceBitAndS::new(Expr::as_dyn_int(self)).to_expr() +impl ReduceBitsImpl for SIntType { + fn value_reduce_bitand( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + value.bits().all().into() } - fn reduce_bitor(self) -> Self::UIntOutput { - ReduceBitOrS::new(Expr::as_dyn_int(self)).to_expr() + fn value_reduce_bitor(_this: Self, value: Cow<'_, Self::SimValue>) -> UIntValue> { + value.bits().any().into() } - fn reduce_bitxor(self) -> Self::UIntOutput { - ReduceBitXorS::new(Expr::as_dyn_int(self)).to_expr() + fn value_reduce_bitxor( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + (value.bits().count_ones() % 2 != 0).into() } - fn any_one_bits(self) -> Self::BoolOutput { - self.reduce_bitor().cast_to_static() + fn expr_reduce_bitand(this: Expr) -> Expr> { + ReduceBitAndS::new(Expr::as_dyn_int(this)).to_expr() } - fn any_zero_bits(self) -> Self::BoolOutput { - (!self.reduce_bitand()).cast_to_static() + fn expr_reduce_bitor(this: Expr) -> Expr> { + ReduceBitOrS::new(Expr::as_dyn_int(this)).to_expr() } - fn all_one_bits(self) -> Self::BoolOutput { - self.reduce_bitand().cast_to_static() - } - - fn all_zero_bits(self) -> Self::BoolOutput { - (!self.reduce_bitor()).cast_to_static() - } - - fn parity_odd(self) -> Self::BoolOutput { - self.reduce_bitxor().cast_to_static() - } - - fn parity_even(self) -> Self::BoolOutput { - (!self.reduce_bitxor()).cast_to_static() + fn expr_reduce_bitxor(this: Expr) -> Expr> { + ReduceBitXorS::new(Expr::as_dyn_int(this)).to_expr() } } -impl ReduceBits for Expr { - type UIntOutput = Expr>; - type BoolOutput = Expr; - - fn reduce_bitand(self) -> Self::UIntOutput { - self.cast_to_static() +impl ReduceBitsImpl for Bool { + fn value_reduce_bitand( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + (*value).into() } - fn reduce_bitor(self) -> Self::UIntOutput { - self.cast_to_static() + fn value_reduce_bitor(_this: Self, value: Cow<'_, Self::SimValue>) -> UIntValue> { + (*value).into() } - fn reduce_bitxor(self) -> Self::UIntOutput { - self.cast_to_static() + fn value_reduce_bitxor( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + (*value).into() } - fn any_one_bits(self) -> Self::BoolOutput { - self + fn expr_reduce_bitand(this: Expr) -> Expr> { + this.cast_to_static() } - fn any_zero_bits(self) -> Self::BoolOutput { - !self + fn expr_reduce_bitor(this: Expr) -> Expr> { + this.cast_to_static() } - fn all_one_bits(self) -> Self::BoolOutput { - self - } - - fn all_zero_bits(self) -> Self::BoolOutput { - !self - } - - fn parity_odd(self) -> Self::BoolOutput { - self - } - - fn parity_even(self) -> Self::BoolOutput { - !self + fn expr_reduce_bitxor(this: Expr) -> Expr> { + this.cast_to_static() } } @@ -2629,13 +4532,20 @@ impl ToLiteralBits for CastToBits { impl_get_target_none!([] CastToBits); -impl ToExpr for CastToBits { +impl ValueType for CastToBits { type Type = UInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + UInt::new_dyn(self.arg.ty().bit_width()) + } +} + +impl ToExpr for CastToBits { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::CastToBits(*self).intern(), - __ty: UInt::new_dyn(Expr::ty(self.arg).bit_width()), + __ty: self.ty(), __flow: Flow::Source, } } @@ -2653,7 +4563,7 @@ impl CastBitsTo { pub fn new(arg: Expr, ty: T) -> Self { let ty_props = ty.canonical().type_properties(); assert!(ty_props.is_castable_from_bits); - assert_eq!(Expr::ty(arg).width(), ty_props.bit_width); + assert_eq!(arg.ty().width(), ty_props.bit_width); Self { arg, ty, @@ -2663,9 +4573,6 @@ impl CastBitsTo { pub fn arg(self) -> Expr { self.arg } - pub fn ty(self) -> T { - self.ty - } } impl ToLiteralBits for CastBitsTo { @@ -2676,9 +4583,16 @@ impl ToLiteralBits for CastBitsTo { impl_get_target_none!([T: Type] CastBitsTo); -impl ToExpr for CastBitsTo { +impl ValueType for CastBitsTo { type Type = T; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for CastBitsTo { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::CastBitsTo(CastBitsTo { @@ -2703,9 +4617,6 @@ impl Uninit { pub fn new(ty: T) -> Self { Self { ty } } - pub fn ty(self) -> T { - self.ty - } } impl ToLiteralBits for Uninit { @@ -2716,9 +4627,16 @@ impl ToLiteralBits for Uninit { impl_get_target_none!([T: Type] Uninit); -impl ToExpr for Uninit { +impl ValueType for Uninit { type Type = T; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for Uninit { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::Uninit(Uninit { diff --git a/crates/fayalite/src/expr/ops/test_ops_impls.rs b/crates/fayalite/src/expr/ops/test_ops_impls.rs new file mode 100644 index 00000000..d29aff0e --- /dev/null +++ b/crates/fayalite/src/expr/ops/test_ops_impls.rs @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{expr::Valueless, expr::ops::make_impls, prelude::*}; +use std::num::NonZero; + +macro_rules! assert_neg_impls { + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => { + const _: () = { + fn _check_neg_impl<$($Lifetimes)*$($Bounds)*>(v: $ty) + -> impl ValueType< + ValueCategory = <$ty as ValueType>::ValueCategory, + Type = <::Type> as std::ops::Neg>::Output as ValueType>::Type, + > { + std::ops::Neg::neg(v) + } + }; + }; +} + +make_impls! { + #[kinds((sint_local<'a, Width>))] + assert_neg_impls! {} +} + +macro_rules! assert_not_impls { + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => { + const _: () = { + fn _check_not_impl<$($Lifetimes)*$($Bounds)*>(v: $ty) + -> impl ValueType< + ValueCategory = <$ty as ValueType>::ValueCategory, + Type = <::Type> as std::ops::Not>::Output as ValueType>::Type, + > { + std::ops::Not::not(v) + } + }; + }; +} + +make_impls! { + #[kinds((int_local<'a, Width>), (bool<'a>))] + assert_not_impls! {} +} + +macro_rules! assert_cast_to_bits_impls { + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => { + const _: () = { + fn _check_cast_to_bits_impl<$($Lifetimes)*$($Bounds)*>(v: $ty) + -> impl ValueType< + ValueCategory = <$ty as ValueType>::ValueCategory, + Type = <::Type> as CastToBits>::Output as ValueType>::Type, + > { + <$ty as CastToBits>::cast_to_bits(&v) + } + }; + }; +} + +make_impls! { + #[kinds((uint<'a, Width>), (sint<'a, Width>), (bool<'a>))] + assert_cast_to_bits_impls! {} +} + +macro_rules! assert_simple_bin_op_impls { + ([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), !$Trait:ident::$f:ident) => { + #[expect(dead_code)] + const _: () = { + trait HasImpl { + fn check_impl(self); + } + + trait NoImpl { + fn check_impl(&self) -> &'static dyn NoImpl; + } + + impl NoImpl for (L, R) { + fn check_impl(&self) -> &'static dyn NoImpl { + unreachable!() + } + } + + impl, R> HasImpl for (L, R) { + fn check_impl(self) {} + } + + fn check_simple_bin_op_no_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: $($R)*) { + let _: &'static dyn NoImpl = (l, r).check_impl(); + } + }; + }; + ([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] (usize), Shl::shl) => { + const _: () = { + #[expect(dead_code)] + fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: usize) + -> impl ValueType< + ValueCategory = <($($L)*, usize) as ValueType>::ValueCategory, + Type = <::Type> as std::ops::Shl>::Output as ValueType>::Type, + > { + std::ops::Shl::shl(l, r) + } + }; + }; + ([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] (usize), Shr::shr) => { + const _: () = { + #[expect(dead_code)] + fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: usize) + -> impl ValueType< + ValueCategory = <($($L)*, usize) as ValueType>::ValueCategory, + Type = <::Type> as std::ops::Shr>::Output as ValueType>::Type, + > { + std::ops::Shr::shr(l, r) + } + }; + }; + ([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), $Trait:ident::$f:ident) => { + const _: () = { + #[expect(dead_code)] + fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: $($R)*) + -> impl ValueType< + ValueCategory = <($($L)*, $($R)*) as ValueType>::ValueCategory, + Type = <::Type> as std::ops::$Trait::Type>>>::Output as ValueType>::Type, + > { + std::ops::$Trait::$f(l, r) + } + }; + }; + ($LLifetimes:tt $LBounds:tt ($($L:tt)*), $RLifetimes:tt $RBounds:tt ($($R:tt)*), !$FirstTrait:ident::$first_f:ident, $($rest:tt)*) => { + assert_simple_bin_op_impls! { + $LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), !$FirstTrait::$first_f + } + assert_simple_bin_op_impls! { + $LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $($rest)* + } + }; + ($LLifetimes:tt $LBounds:tt ($($L:tt)*), $RLifetimes:tt $RBounds:tt ($($R:tt)*), $FirstTrait:ident::$first_f:ident, $($rest:tt)*) => { + assert_simple_bin_op_impls! { + $LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $FirstTrait::$first_f + } + assert_simple_bin_op_impls! { + $LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $($rest)* + } + }; +} + +make_impls! { + #[kinds((uint_local<'l, L>))] + #[kinds((uint<'r, R>))] + assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((uint<'l, L>))] + #[kinds((uint_local<'r, R>))] + assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor, Shl::shl, Shr::shr} +} + +make_impls! { + #[kinds((sint_local<'l, L>))] + #[kinds((sint<'r, R>))] + assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((sint<'l, L>))] + #[kinds((sint_local<'r, R>))] + assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((sint<'l, L>))] + #[kinds((uint_local<'r, R>))] + assert_simple_bin_op_impls! {Shl::shl, Shr::shr} +} + +make_impls! { + #[kinds((uint_local<'l, L>))] + assert_simple_bin_op_impls! {[][] (usize), Shl::shl, Shr::shr} +} + +make_impls! { + #[kinds((sint_local<'l, L>))] + assert_simple_bin_op_impls! {[][] (usize), Shl::shl, Shr::shr} +} + +make_impls! { + #[kinds((bool_local<'l>))] + #[kinds((bool_local<'r>))] + assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((bool<'l>))] + #[kinds((Valueless))] + assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((Valueless))] + #[kinds((bool<'r>))] + assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((bool_at_most_sim_value<'l>))] + #[kinds((bool_at_most_sim_value<'r>))] + assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +// assert there are no impls of BitAnd/BitOr/BitXor between Expr and Rust's bool, +// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do +// what is usually wanted. +make_impls! { + #[kinds((Expr))] + #[kinds(bool)] + assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor} +} + +make_impls! { + #[kinds(bool)] + #[kinds((Expr))] + assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor} +} diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index c8c55e93..95d8e0f5 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -3,7 +3,7 @@ use crate::{ array::Array, bundle::{Bundle, BundleField}, - expr::{Expr, Flow, ToExpr}, + expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr}, intern::{Intern, Interned}, memory::{DynPortType, MemPort}, module::{Instance, ModuleIO, TargetName}, @@ -196,9 +196,18 @@ macro_rules! impl_target_base { } } - impl ToExpr for $TargetBase { + impl ValueType for $TargetBase { type Type = CanonicalType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + match self { + $(Self::$Variant(v) => v.ty().canonical(),)* + } + } + } + + impl ToExpr for $TargetBase { fn to_expr(&self) -> Expr { match self { $(Self::$Variant(v) => Expr::canonical(v.to_expr()),)* diff --git a/crates/fayalite/src/expr/value_category.rs b/crates/fayalite/src/expr/value_category.rs new file mode 100644 index 00000000..548ae7bf --- /dev/null +++ b/crates/fayalite/src/expr/value_category.rs @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{expr::ValueType, ty::Type}; + +trait ValueCategorySealed {} + +#[expect(private_bounds)] +pub trait ValueCategory: + ValueCategorySealed + + Copy + + Ord + + Default + + std::fmt::Debug + + std::hash::Hash + + 'static + + Send + + Sync +{ + type DispatchOutput>: ValueType; + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput; + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum; +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +pub struct ValueCategoryValue; + +impl ValueCategorySealed for ValueCategoryValue {} + +impl ValueCategory for ValueCategoryValue { + type DispatchOutput> = D::Value; + + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput { + dispatch.dispatch_value() + } + + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum { + ValueCategoryDispatchOutputEnum::Value(dispatch.dispatch_value()) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +pub struct ValueCategorySimValue; + +impl ValueCategorySealed for ValueCategorySimValue {} + +impl ValueCategory for ValueCategorySimValue { + type DispatchOutput> = D::SimValue; + + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput { + dispatch.dispatch_sim_value() + } + + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum { + ValueCategoryDispatchOutputEnum::SimValue(dispatch.dispatch_sim_value()) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +pub struct ValueCategoryExpr; + +impl ValueCategorySealed for ValueCategoryExpr {} + +impl ValueCategory for ValueCategoryExpr { + type DispatchOutput> = D::Expr; + + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput { + dispatch.dispatch_expr() + } + + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum { + ValueCategoryDispatchOutputEnum::Expr(dispatch.dispatch_expr()) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +pub struct ValueCategoryValueless; + +impl ValueCategorySealed for ValueCategoryValueless {} + +impl ValueCategory for ValueCategoryValueless { + type DispatchOutput> = D::Valueless; + + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput { + dispatch.dispatch_valueless() + } + + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum { + ValueCategoryDispatchOutputEnum::Valueless(dispatch.dispatch_valueless()) + } +} + +pub trait ValueCategoryIsValue: ValueCategoryIsValueOrSimValue {} + +impl ValueCategoryIsValue for ValueCategoryValue {} + +pub trait ValueCategoryIsValueOrSimValue: ValueCategoryIsValueSimValueOrExpr {} + +impl ValueCategoryIsValueOrSimValue for ValueCategoryValue {} +impl ValueCategoryIsValueOrSimValue for ValueCategorySimValue {} + +pub trait ValueCategoryIsValueSimValueOrExpr: ValueCategoryIsValueSimValueExprOrValueless {} + +impl ValueCategoryIsValueSimValueOrExpr for ValueCategoryValue {} +impl ValueCategoryIsValueSimValueOrExpr for ValueCategorySimValue {} +impl ValueCategoryIsValueSimValueOrExpr for ValueCategoryExpr {} + +pub trait ValueCategoryIsValueSimValueExprOrValueless: ValueCategory {} + +impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryValue {} +impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategorySimValue {} +impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryExpr {} +impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryValueless {} + +pub trait ValueCategoryIsValueless: ValueCategoryIsExprOrValueless {} + +impl ValueCategoryIsValueless for ValueCategoryValueless {} + +pub trait ValueCategoryIsExprOrValueless: ValueCategoryIsValueSimValueExprOrValueless {} + +impl ValueCategoryIsExprOrValueless for ValueCategoryExpr {} +impl ValueCategoryIsExprOrValueless for ValueCategoryValueless {} + +pub trait ValueCategoryIsSimValueExprOrValueless: + ValueCategoryIsValueSimValueExprOrValueless +{ +} + +impl ValueCategoryIsSimValueExprOrValueless for ValueCategorySimValue {} +impl ValueCategoryIsSimValueExprOrValueless for ValueCategoryExpr {} +impl ValueCategoryIsSimValueExprOrValueless for ValueCategoryValueless {} + +trait ValueCategoryCommonSealed {} + +#[expect(private_bounds)] +pub trait ValueCategoryCommon< + T: ValueCategoryCommonSealed + + Copy + + Ord + + Default + + std::fmt::Debug + + std::hash::Hash + + 'static + + Send + + Sync, +>: ValueCategory +{ + type Common: ValueCategory; +} + +macro_rules! impl_value_category_common { + ($($T:ident),* $(,)?) => { + impl_value_category_common!([$($T,)*]); + }; + ([$($A:ident,)?]) => {}; + ([$FirstT:ident, $($T:ident,)+]) => { + impl_value_category_common!([$($T,)*]); + + impl<$FirstT: ValueCategory, $($T: ValueCategory),*> ValueCategoryCommonSealed for ($FirstT, $($T),*) {} + + impl ValueCategoryCommon<($FirstT, $($T),*)> for This + where + $FirstT: ValueCategory, + $($T: ValueCategory,)* + This: ValueCategoryCommon<($FirstT,), Common: ValueCategoryCommon<($($T,)*)>>, + { + type Common = <>::Common as ValueCategoryCommon<($($T,)*)>>::Common; + } + }; +} + +impl_value_category_common!(T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0); + +impl ValueCategoryCommonSealed for (T,) {} + +impl ValueCategoryCommonSealed for () {} + +impl ValueCategoryCommon<()> for T { + type Common = T; +} + +impl ValueCategoryCommon<(ValueCategoryValue,)> for T { + type Common = ValueCategoryValue; +} + +impl ValueCategoryCommon<(ValueCategorySimValue,)> for T { + type Common = ValueCategorySimValue; +} + +impl ValueCategoryCommon<(T,)> for ValueCategorySimValue { + type Common = ValueCategorySimValue; +} + +impl ValueCategoryCommon<(ValueCategoryExpr,)> for T { + type Common = ValueCategoryExpr; +} + +impl ValueCategoryCommon<(T,)> for ValueCategoryExpr { + type Common = ValueCategoryExpr; +} + +impl ValueCategoryCommon<(ValueCategoryValueless,)> + for T +{ + type Common = ValueCategoryValueless; +} + +impl ValueCategoryCommon<(T,)> for ValueCategoryValueless { + type Common = ValueCategoryValueless; +} + +pub trait ValueCategoryDispatch: Sized { + type Type: Type; + type InputValueCategory: ValueCategory; + type Value: ValueType; + type SimValue: ValueType; + type Expr: ValueType; + type Valueless: ValueType; + fn dispatch_value(self) -> Self::Value + where + Self: ValueCategoryDispatch; + fn dispatch_sim_value(self) -> Self::SimValue + where + Self: ValueCategoryDispatch; + fn dispatch_expr(self) -> Self::Expr + where + Self: ValueCategoryDispatch; + fn dispatch_valueless(self) -> Self::Valueless + where + Self: ValueCategoryDispatch; + fn dispatch(self) -> ::DispatchOutput { + Self::InputValueCategory::dispatch(self) + } + fn dispatch_enum(self) -> ValueCategoryDispatchOutputEnum { + Self::InputValueCategory::dispatch_enum(self) + } +} + +pub enum ValueCategoryDispatchOutputEnum { + Value(T::Value), + SimValue(T::SimValue), + Expr(T::Expr), + Valueless(T::Valueless), +} + +impl ValueCategoryDispatchOutputEnum { + pub fn try_into_value(self) -> Result { + if let Self::Value(v) = self { + Ok(v) + } else { + Err(self) + } + } + pub fn try_into_sim_value(self) -> Result { + if let Self::SimValue(v) = self { + Ok(v) + } else { + Err(self) + } + } + pub fn try_into_expr(self) -> Result { + if let Self::Expr(v) = self { + Ok(v) + } else { + Err(self) + } + } + pub fn try_into_valueless(self) -> Result { + if let Self::Valueless(v) = self { + Ok(v) + } else { + Err(self) + } + } + #[track_caller] + #[cold] + fn unwrap_invalid(self, expected_kind: &'static str) -> ! { + match self { + Self::Value(_) => panic!("expected {expected_kind}, got Value"), + Self::SimValue(_) => panic!("expected {expected_kind}, got SimValue"), + Self::Expr(_) => panic!("expected {expected_kind}, got Expr"), + Self::Valueless(_) => panic!("expected {expected_kind}, got Valueless"), + } + } + #[track_caller] + pub fn into_value_unwrap(self) -> T::Value { + let Self::Value(v) = self else { + self.unwrap_invalid("Value"); + }; + v + } + #[track_caller] + pub fn into_sim_value_unwrap(self) -> T::SimValue { + let Self::SimValue(v) = self else { + self.unwrap_invalid("SimValue"); + }; + v + } + #[track_caller] + pub fn into_expr_unwrap(self) -> T::Expr { + let Self::Expr(v) = self else { + self.unwrap_invalid("Expr"); + }; + v + } + #[track_caller] + pub fn into_valueless_unwrap(self) -> T::Valueless { + let Self::Valueless(v) = self else { + self.unwrap_invalid("Valueless"); + }; + v + } + pub fn ty(&self) -> T::Type { + match self { + Self::Value(v) => v.ty(), + Self::SimValue(v) => v.ty(), + Self::Expr(v) => v.ty(), + Self::Valueless(v) => v.ty(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + type Identity = T; + + macro_rules! check_categories { + ($($Categories:ident,)+) => { + check_categories!([] [$($Categories,)+]); + }; + ([$($Categories:ident,)*] []) => {}; + ([$($WeakerCategories:ident,)*] [$Category:ident, $($StrongerCategories:ident,)*]) => { + $(const _: $Category = Identity::<<$WeakerCategories as ValueCategoryCommon<($Category,)>>::Common> {}; + const _: $Category = Identity::<<$Category as ValueCategoryCommon<($WeakerCategories,)>>::Common> {};)* + const _: $Category = Identity::<<$Category as ValueCategoryCommon<($Category,)>>::Common> {}; + check_categories!([$($WeakerCategories,)* $Category,] [$($StrongerCategories,)*]); + }; + } + + check_categories!( + ValueCategoryValue, + ValueCategorySimValue, + ValueCategoryExpr, + ValueCategoryValueless, + ); +} diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index b4a1d146..383bd95a 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -4,14 +4,15 @@ use crate::{ annotations::{ Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, - DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, + DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation, }, array::Array, + build::{ToArgs, WriteArgs}, bundle::{Bundle, BundleField, BundleType}, clock::Clock, enum_::{Enum, EnumType, EnumVariant}, expr::{ - CastBitsTo, Expr, ExprEnum, + CastBitsTo, Expr, ExprEnum, ToExpr, ValueType, ops::{self, VariantAccess}, target::{ Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, @@ -23,9 +24,9 @@ use crate::{ memory::{Mem, PortKind, PortName, ReadUnderWrite}, module::{ AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter, - ExternModuleParameterValue, Module, ModuleBody, NameOptId, NormalModuleBody, Stmt, - StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, - StmtWire, + ExternModuleParameterValue, Module, ModuleBody, ModuleIO, NameId, NameOptId, + NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, + StmtMatch, StmtReg, StmtWire, transform::{ simplify_enums::{SimplifyEnumsError, SimplifyEnumsKind, simplify_enums}, simplify_memories::simplify_memories, @@ -38,21 +39,23 @@ use crate::{ BitSliceWriteWithBase, DebugAsRawString, GenericConstBool, HashMap, HashSet, const_str_array_is_strictly_ascending, }, + vendor::xilinx::XilinxAnnotation, }; use bitvec::slice::BitSlice; use clap::value_parser; use num_traits::Signed; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::{ cell::{Cell, RefCell}, cmp::Ordering, collections::{BTreeMap, VecDeque}, error::Error, + ffi::OsString, fmt::{self, Write}, fs, hash::Hash, io, - ops::Range, + ops::{ControlFlow, Range}, path::{Path, PathBuf}, rc::Rc, }; @@ -404,10 +407,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) -> Result { + 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) -> Result<(Ident, Rc>)> { + fn bundle_def(&self, ty: Bundle) -> Result<(Ident, Rc>), FirrtlError> { self.bundle_defs.get_or_make(ty, |&ty, definitions| { let mut ns = Namespace::default(); let mut body = String::new(); @@ -428,13 +431,13 @@ impl TypeState { Ok((name, Rc::new(RefCell::new(ns)))) }) } - fn bundle_ty(&self, ty: Bundle) -> Result { + fn bundle_ty(&self, ty: Bundle) -> Result { Ok(self.bundle_def(ty)?.0) } - fn bundle_ns(&self, ty: Bundle) -> Result>> { + fn bundle_ns(&self, ty: Bundle) -> Result>, FirrtlError> { Ok(self.bundle_def(ty)?.1) } - fn enum_def(&self, ty: Enum) -> Result<(Ident, Rc)> { + fn enum_def(&self, ty: Enum) -> Result<(Ident, Rc), FirrtlError> { self.enum_defs.get_or_make(ty, |&ty, definitions| { let mut variants = Namespace::default(); let mut body = String::new(); @@ -461,13 +464,13 @@ impl TypeState { )) }) } - fn enum_ty(&self, ty: Enum) -> Result { + fn enum_ty(&self, ty: Enum) -> Result { Ok(self.enum_def(ty)?.0) } - fn get_enum_variant(&mut self, ty: Enum, name: Interned) -> Result { + 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) -> Result { + 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(), @@ -485,7 +488,7 @@ impl TypeState { CanonicalType::Reset(Reset {}) => "Reset".into(), CanonicalType::PhantomConst(_) => "{}".into(), CanonicalType::DynSimOnly(_) => { - return Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()); + return Err(FirrtlError::SimOnlyValuesAreNotPermitted); } }) } @@ -874,7 +877,7 @@ impl<'a> Exporter<'a> { definitions: &RcDefinitions, const_ty: bool, ) -> Result { - let from_ty = Expr::ty(value); + let from_ty = value.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 @@ -923,7 +926,7 @@ impl<'a> Exporter<'a> { definitions: &RcDefinitions, const_ty: bool, ) -> Result { - let base_width = Expr::ty(base).width(); + let base_width = base.ty().width(); let base = self.expr(Expr::canonical(base), definitions, const_ty)?; if range.is_empty() { Ok(format!("tail({base}, {base_width})")) @@ -1206,9 +1209,7 @@ impl<'a> Exporter<'a> { | CanonicalType::AsyncReset(_) | CanonicalType::Reset(_) => Ok(format!("asUInt({value_str})")), CanonicalType::PhantomConst(_) => Ok("UInt<0>(0)".into()), - CanonicalType::DynSimOnly(_) => { - Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()) - } + CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()), } } fn expr_cast_bits_to_bundle( @@ -1429,9 +1430,7 @@ impl<'a> Exporter<'a> { definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); return Ok(retval.to_string()); } - CanonicalType::DynSimOnly(_) => { - Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()) - } + CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()), } } fn expr_unary( @@ -1471,7 +1470,7 @@ impl<'a> Exporter<'a> { 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()), + UInt[0].zero().to_expr().cast_bits_to(ty.canonical()), definitions, const_ty, ), @@ -1654,7 +1653,7 @@ impl<'a> Exporter<'a> { let value_str = self.expr(expr.arg(), definitions, const_ty)?; self.expr_cast_to_bits( value_str, - Expr::ty(expr.arg()), + expr.arg().ty(), definitions, Indent { indent_depth: &Cell::new(0), @@ -1777,7 +1776,7 @@ impl<'a> Exporter<'a> { 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.base().ty(), expr.field_name())?; write!(out, ".{name}").unwrap(); Ok(out) } @@ -1884,7 +1883,11 @@ impl<'a> Exporter<'a> { } fn annotation(&mut self, path: AnnotationTargetPath, annotation: &Annotation) { let data = match annotation { - Annotation::DontTouch(DontTouchAnnotation {}) => AnnotationData::DontTouch, + Annotation::DontTouch(DontTouchAnnotation {}) => { + // TODO: error if the annotated thing was renamed because of a naming conflict, + // unless Target::base() is one of the ports of the top-level module since that's handled by ScalarizedModuleABI + AnnotationData::DontTouch + } Annotation::SVAttribute(SVAttributeAnnotation { text }) => { AnnotationData::AttributeAnnotation { description: *text } } @@ -1907,6 +1910,9 @@ impl<'a> Exporter<'a> { class: str::to_string(class), additional_fields: (*additional_fields).into(), }, + Annotation::Xilinx(XilinxAnnotation::XdcLocation(_)) + | Annotation::Xilinx(XilinxAnnotation::XdcIOStandard(_)) + | Annotation::Xilinx(XilinxAnnotation::XdcCreateClock(_)) => return, }; self.annotations.push(FirrtlAnnotation { data, @@ -2095,12 +2101,12 @@ impl<'a> Exporter<'a> { rhs, source_location, }) => { - if Expr::ty(lhs) != Expr::ty(rhs) { + if lhs.ty() != rhs.ty() { writeln!( body, "{indent}; connect different types:\n{indent}; lhs: {:?}\n{indent}; rhs: {:?}", - Expr::ty(lhs), - Expr::ty(rhs), + lhs.ty(), + rhs.ty(), ) .unwrap(); } @@ -2194,7 +2200,7 @@ impl<'a> Exporter<'a> { FileInfo::new(source_location), ) .unwrap(); - let enum_ty = Expr::ty(expr); + let enum_ty = expr.ty(); let match_arms_indent = indent.push(); for ((variant_index, variant), match_arm_block) in enum_ty.variants().iter().enumerate().zip(blocks) @@ -2320,6 +2326,7 @@ impl<'a> Exporter<'a> { ModuleBody::Extern(ExternModuleBody { verilog_name, parameters, + clocks_for_past: _, simulation: _, }) => { let verilog_name = Ident(verilog_name); @@ -2454,7 +2461,7 @@ impl FileBackendTrait for &'_ mut T { pub struct FileBackend { pub dir_path: PathBuf, pub circuit_name: Option, - pub top_fir_file_stem: Option, + pub top_fir_file_stem: Option, } impl FileBackend { @@ -2503,7 +2510,7 @@ impl FileBackendTrait for FileBackend { ) -> Result<(), Self::Error> { let top_fir_file_stem = self .top_fir_file_stem - .get_or_insert_with(|| circuit_name.clone()); + .get_or_insert_with(|| circuit_name.clone().into()); self.circuit_name = Some(circuit_name); let mut path = self.dir_path.join(top_fir_file_stem); if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) { @@ -2677,21 +2684,12 @@ impl FileBackendTrait for TestBackend { fn export_impl( file_backend: &mut dyn WrappedFileBackendTrait, - mut top_module: Interned>, + top_module: Interned>, options: ExportOptions, ) -> Result<(), WrappedError> { - let ExportOptions { - simplify_memories: do_simplify_memories, - simplify_enums: do_simplify_enums, - __private: _, - } = options; - if let Some(kind) = do_simplify_enums { - top_module = - simplify_enums(top_module, kind).map_err(|e| file_backend.simplify_enums_error(e))?; - } - if do_simplify_memories { - top_module = simplify_memories(top_module); - } + let top_module = options + .prepare_top_module(top_module) + .map_err(|e| file_backend.simplify_enums_error(e))?; let indent_depth = Cell::new(0); let mut global_ns = Namespace::default(); let circuit_name = global_ns.get(top_module.name_id()); @@ -2753,14 +2751,23 @@ impl clap::builder::TypedValueParser for OptionSimplifyEnumsKindValueParser { #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ExportOptionsPrivate(()); -#[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash)] +impl ExportOptionsPrivate { + fn private_new() -> Self { + Self(()) + } +} + +#[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ExportOptions { #[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)] + #[serde(default = "ExportOptions::default_simplify_memories")] pub simplify_memories: bool, #[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = "replace-with-bundle-of-uints")] - pub simplify_enums: std::option::Option, + #[serde(default = "ExportOptions::default_simplify_enums")] + pub simplify_enums: std::option::Option, // use std::option::Option instead of Option to avoid clap mis-parsing #[doc(hidden)] #[clap(skip = ExportOptionsPrivate(()))] + #[serde(skip, default = "ExportOptionsPrivate::private_new")] /// `#[non_exhaustive]` except allowing struct update syntax pub __private: ExportOptionsPrivate, } @@ -2771,7 +2778,34 @@ impl fmt::Debug for ExportOptions { } } +impl ToArgs for ExportOptions { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { + simplify_memories, + simplify_enums, + __private: ExportOptionsPrivate(()), + } = *self; + if !simplify_memories { + args.write_arg("--no-simplify-memories"); + } + let simplify_enums = simplify_enums.map(|v| { + clap::ValueEnum::to_possible_value(&v).expect("there are no skipped variants") + }); + let simplify_enums = match &simplify_enums { + None => OptionSimplifyEnumsKindValueParser::NONE_NAME, + Some(v) => v.get_name(), + }; + args.write_long_option_eq("simplify-enums", simplify_enums); + } +} + impl ExportOptions { + fn default_simplify_memories() -> bool { + true + } + fn default_simplify_enums() -> Option { + Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts) + } fn debug_fmt( &self, f: &mut fmt::Formatter<'_>, @@ -2823,18 +2857,47 @@ impl ExportOptions { if f.alternate() { "\n}" } else { " }" } ) } + fn prepare_top_module_helper( + self, + mut top_module: Interned>, + ) -> Result>, SimplifyEnumsError> { + let Self { + simplify_memories: do_simplify_memories, + simplify_enums: do_simplify_enums, + __private: _, + } = self; + if let Some(kind) = do_simplify_enums { + top_module = simplify_enums(top_module, kind)?; + } + if do_simplify_memories { + top_module = simplify_memories(top_module); + } + Ok(top_module) + } + pub fn prepare_top_module( + self, + top_module: impl AsRef>, + ) -> Result>, SimplifyEnumsError> { + self.prepare_top_module_helper(top_module.as_ref().canonical().intern()) + } } impl Default for ExportOptions { fn default() -> Self { Self { - simplify_memories: true, - simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), + simplify_memories: Self::default_simplify_memories(), + simplify_enums: Self::default_simplify_enums(), __private: ExportOptionsPrivate(()), } } } +pub fn get_circuit_name(top_module_name_id: NameId) -> Interned { + let mut global_ns = Namespace::default(); + let circuit_name = global_ns.get(top_module_name_id); + circuit_name.0 +} + pub fn export( file_backend: B, top_module: &Module, @@ -2846,6 +2909,497 @@ pub fn export( }) } +#[derive(Debug)] +#[non_exhaustive] +pub enum ScalarizedModuleABIError { + SimOnlyValuesAreNotPermitted, + SimplifyEnumsError(SimplifyEnumsError), +} + +impl fmt::Display for ScalarizedModuleABIError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted => { + FirrtlError::SimOnlyValuesAreNotPermitted.fmt(f) + } + ScalarizedModuleABIError::SimplifyEnumsError(e) => e.fmt(f), + } + } +} + +impl std::error::Error for ScalarizedModuleABIError {} + +impl From for ScalarizedModuleABIError { + fn from(value: SimplifyEnumsError) -> Self { + Self::SimplifyEnumsError(value) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum ScalarizedModuleABIPortItem { + Group(ScalarizedModuleABIPortGroup), + Port(ScalarizedModuleABIPort), +} + +impl ScalarizedModuleABIPortItem { + pub fn module_io(self) -> ModuleIO { + *self + .target() + .base() + .module_io() + .expect("known to be ModuleIO") + } + pub fn target(self) -> Interned { + match self { + Self::Group(v) => v.target(), + Self::Port(v) => v.target(), + } + } + fn for_each_port_and_annotations_helper< + F: for<'a> FnMut( + &'a ScalarizedModuleABIPort, + ScalarizedModuleABIAnnotations<'a>, + ) -> ControlFlow, + B, + >( + &self, + parent: Option<&ScalarizedModuleABIAnnotations<'_>>, + f: &mut F, + ) -> ControlFlow { + match self { + Self::Group(v) => v.for_each_port_and_annotations_helper(parent, f), + Self::Port(port) => f( + port, + ScalarizedModuleABIAnnotations::new(parent, port.annotations.iter()), + ), + } + } + pub fn for_each_port_and_annotations< + F: for<'a> FnMut( + &'a ScalarizedModuleABIPort, + ScalarizedModuleABIAnnotations<'a>, + ) -> ControlFlow, + B, + >( + self, + mut f: F, + ) -> ControlFlow { + self.for_each_port_and_annotations_helper(None, &mut f) + } +} + +impl fmt::Debug for ScalarizedModuleABIPortItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Group(v) => v.fmt(f), + Self::Port(v) => v.fmt(f), + } + } +} + +#[derive(Debug, Clone)] +pub struct ScalarizedModuleABIAnnotations<'a> { + parent: Option<&'a ScalarizedModuleABIAnnotations<'a>>, + parent_len: usize, + annotations: std::slice::Iter<'a, TargetedAnnotation>, +} + +impl<'a> ScalarizedModuleABIAnnotations<'a> { + fn new( + parent: Option<&'a ScalarizedModuleABIAnnotations<'a>>, + annotations: std::slice::Iter<'a, TargetedAnnotation>, + ) -> Self { + Self { + parent, + parent_len: parent.map_or(0, |parent| parent.len()), + annotations, + } + } +} + +impl<'a> Default for ScalarizedModuleABIAnnotations<'a> { + fn default() -> Self { + Self { + parent: None, + parent_len: 0, + annotations: Default::default(), + } + } +} + +impl<'a> Iterator for ScalarizedModuleABIAnnotations<'a> { + type Item = &'a TargetedAnnotation; + + fn next(&mut self) -> Option { + loop { + if let retval @ Some(_) = self.annotations.next() { + break retval; + } + *self = self.parent?.clone(); + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } + + fn fold(mut self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + loop { + let Self { + parent, + parent_len: _, + annotations, + } = self; + init = annotations.fold(init, &mut f); + let Some(next) = parent else { + break; + }; + self = next.clone(); + } + init + } +} + +impl std::iter::FusedIterator for ScalarizedModuleABIAnnotations<'_> {} + +impl ExactSizeIterator for ScalarizedModuleABIAnnotations<'_> { + fn len(&self) -> usize { + self.parent_len + self.annotations.len() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ScalarizedModuleABIPortGroup { + target: Interned, + common_annotations: Interned<[TargetedAnnotation]>, + children: Interned<[ScalarizedModuleABIPortItem]>, +} + +impl ScalarizedModuleABIPortGroup { + pub fn module_io(self) -> ModuleIO { + *self + .target + .base() + .module_io() + .expect("known to be ModuleIO") + } + pub fn target(self) -> Interned { + self.target + } + pub fn common_annotations(self) -> Interned<[TargetedAnnotation]> { + self.common_annotations + } + pub fn children(self) -> Interned<[ScalarizedModuleABIPortItem]> { + self.children + } + fn for_each_port_and_annotations_helper< + F: for<'a> FnMut( + &'a ScalarizedModuleABIPort, + ScalarizedModuleABIAnnotations<'a>, + ) -> ControlFlow, + B, + >( + &self, + parent: Option<&ScalarizedModuleABIAnnotations<'_>>, + f: &mut F, + ) -> ControlFlow { + let parent = ScalarizedModuleABIAnnotations::new(parent, self.common_annotations.iter()); + for item in &self.children { + item.for_each_port_and_annotations_helper(Some(&parent), f)?; + } + ControlFlow::Continue(()) + } + pub fn for_each_port_and_annotations< + F: for<'a> FnMut( + &'a ScalarizedModuleABIPort, + ScalarizedModuleABIAnnotations<'a>, + ) -> ControlFlow, + B, + >( + self, + mut f: F, + ) -> ControlFlow { + self.for_each_port_and_annotations_helper(None, &mut f) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ScalarizedModuleABIPort { + target: Interned, + annotations: Interned<[TargetedAnnotation]>, + scalarized_name: Interned, +} + +impl ScalarizedModuleABIPort { + pub fn module_io(self) -> ModuleIO { + *self + .target + .base() + .module_io() + .expect("known to be ModuleIO") + } + pub fn target(self) -> Interned { + self.target + } + pub fn annotations(self) -> Interned<[TargetedAnnotation]> { + self.annotations + } + pub fn scalarized_name(self) -> Interned { + self.scalarized_name + } +} + +enum ScalarizeTreeNodeBody { + Leaf { + scalarized_name: Interned, + }, + Bundle { + ty: Bundle, + fields: Vec, + }, + Array { + elements: Vec, + }, +} + +struct ScalarizeTreeNode { + target: Interned, + annotations: Vec, + body: ScalarizeTreeNodeBody, +} + +impl ScalarizeTreeNode { + #[track_caller] + fn find_target(&mut self, annotation_target: Interned) -> &mut Self { + match *annotation_target { + Target::Base(_) => { + assert_eq!( + annotation_target, self.target, + "annotation not on correct ModuleIO", + ); + self + } + Target::Child(target_child) => { + let parent = self.find_target(target_child.parent()); + match *target_child.path_element() { + TargetPathElement::BundleField(TargetPathBundleField { name }) => { + match parent.body { + ScalarizeTreeNodeBody::Leaf { .. } => parent, + ScalarizeTreeNodeBody::Bundle { ty, ref mut fields } => { + &mut fields[ty.name_indexes()[&name]] + } + ScalarizeTreeNodeBody::Array { .. } => { + unreachable!("types are known to match") + } + } + } + TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => { + match parent.body { + ScalarizeTreeNodeBody::Leaf { .. } => parent, + ScalarizeTreeNodeBody::Bundle { .. } => { + unreachable!("types are known to match") + } + ScalarizeTreeNodeBody::Array { ref mut elements } => { + &mut elements[index] + } + } + } + TargetPathElement::DynArrayElement(_) => { + unreachable!("annotations are only on static targets"); + } + } + } + } + } + fn into_scalarized_item(self) -> ScalarizedModuleABIPortItem { + let Self { + target, + annotations, + body, + } = self; + match body { + ScalarizeTreeNodeBody::Leaf { scalarized_name } => { + ScalarizedModuleABIPortItem::Port(ScalarizedModuleABIPort { + target, + annotations: Intern::intern_owned(annotations), + scalarized_name, + }) + } + ScalarizeTreeNodeBody::Bundle { fields: items, .. } + | ScalarizeTreeNodeBody::Array { elements: items } => { + ScalarizedModuleABIPortItem::Group(ScalarizedModuleABIPortGroup { + target, + common_annotations: Intern::intern_owned(annotations), + children: Interned::from_iter( + items.into_iter().map(Self::into_scalarized_item), + ), + }) + } + } + } +} + +#[derive(Default)] +struct ScalarizeTreeBuilder { + scalarized_ns: Namespace, + type_state: TypeState, + name: String, +} + +impl ScalarizeTreeBuilder { + #[track_caller] + fn build_bundle( + &mut self, + target: Interned, + ty: Bundle, + ) -> Result { + let mut fields = Vec::with_capacity(ty.fields().len()); + let original_len = self.name.len(); + for BundleField { name, .. } in ty.fields() { + let firrtl_name = self + .type_state + .get_bundle_field(ty, name) + .map_err(|e| match e { + FirrtlError::SimOnlyValuesAreNotPermitted => { + ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted + } + })? + .0; + write!(self.name, "_{firrtl_name}").expect("writing to String is infallible"); + fields.push( + self.build( + target + .join(TargetPathElement::intern_sized( + TargetPathBundleField { name }.into(), + )) + .intern_sized(), + )?, + ); + self.name.truncate(original_len); + } + Ok(ScalarizeTreeNode { + target, + annotations: Vec::new(), + body: ScalarizeTreeNodeBody::Bundle { ty, fields }, + }) + } + #[track_caller] + fn build( + &mut self, + target: Interned, + ) -> Result { + Ok(match target.canonical_ty() { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::Enum(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => { + let scalarized_name = self.scalarized_ns.get(str::intern(&self.name)).0; + ScalarizeTreeNode { + target, + annotations: Vec::new(), + body: ScalarizeTreeNodeBody::Leaf { scalarized_name }, + } + } + CanonicalType::Array(ty) => { + let mut elements = Vec::with_capacity(ty.len()); + let original_len = self.name.len(); + for index in 0..ty.len() { + write!(self.name, "_{index}").expect("writing to String is infallible"); + elements.push( + self.build( + target + .join(TargetPathElement::intern_sized( + TargetPathArrayElement { index }.into(), + )) + .intern_sized(), + )?, + ); + self.name.truncate(original_len); + } + ScalarizeTreeNode { + target, + annotations: Vec::new(), + body: ScalarizeTreeNodeBody::Array { elements }, + } + } + CanonicalType::Bundle(ty) => self.build_bundle(target, ty)?, + CanonicalType::PhantomConst(_) => { + self.build_bundle(target, Bundle::new(Interned::default()))? + } + CanonicalType::DynSimOnly(_) => { + return Err(ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted); + } + }) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ScalarizedModuleABI { + module_io: Interned<[AnnotatedModuleIO]>, + items: Interned<[ScalarizedModuleABIPortItem]>, +} + +impl ScalarizedModuleABI { + #[track_caller] + fn from_io(module_io: Interned<[AnnotatedModuleIO]>) -> Result { + let mut firrtl_ns = Namespace::default(); + let mut tree_builder = ScalarizeTreeBuilder::default(); + let mut items = Vec::new(); + for module_io in module_io { + let firrtl_name = firrtl_ns.get(module_io.module_io.name_id()); + tree_builder.name.clear(); + tree_builder.name.push_str(&firrtl_name.0); + let mut tree = tree_builder.build(Target::from(module_io.module_io).intern_sized())?; + for annotation in module_io.annotations { + tree.find_target(annotation.target()) + .annotations + .push(annotation); + } + items.push(tree.into_scalarized_item()); + } + Ok(Self { + module_io, + items: Intern::intern_owned(items), + }) + } + #[track_caller] + pub fn new( + module: impl AsRef>, + options: ExportOptions, + ) -> Result { + Self::from_io(options.prepare_top_module(module)?.module_io()) + } + pub fn module_io(&self) -> Interned<[AnnotatedModuleIO]> { + self.module_io + } + pub fn items(&self) -> Interned<[ScalarizedModuleABIPortItem]> { + self.items + } + pub fn for_each_port_and_annotations< + F: for<'a> FnMut( + &'a ScalarizedModuleABIPort, + ScalarizedModuleABIAnnotations<'a>, + ) -> ControlFlow, + B, + >( + self, + mut f: F, + ) -> ControlFlow { + for item in &self.items { + item.for_each_port_and_annotations_helper(None, &mut f)?; + } + ControlFlow::Continue(()) + } +} + #[doc(hidden)] #[track_caller] pub fn assert_export_firrtl_impl(top_module: &Module, expected: TestBackend) { diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 7fa77ce4..2d1f6d2c 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -4,16 +4,19 @@ use crate::{ array::ArrayType, expr::{ - Expr, NotALiteralExpr, ToExpr, ToLiteralBits, + Expr, NotALiteralExpr, ToExpr, ToLiteralBits, ToSimValueInner, ToValueless, ValueType, + Valueless, target::{GetTarget, Target}, + value_category::ValueCategoryValue, }, hdl, intern::{Intern, Interned, Memoize}, sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ - CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, - OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + CanonicalType, FillInDefaultedGenerics, OpaqueSimValueSize, OpaqueSimValueSlice, + OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, TypeProperties, + impl_match_variant_as_self, }, util::{ConstBool, ConstUsize, GenericConstBool, GenericConstUsize, interned_bit, slice_range}, }; @@ -31,7 +34,7 @@ use std::{ num::NonZero, ops::{Index, Not, Range, RangeBounds, RangeInclusive}, str::FromStr, - sync::Arc, + sync::{Arc, OnceLock}, }; mod uint_in_range; @@ -60,8 +63,62 @@ mod sealed { pub const DYN_SIZE: usize = !0; pub type DynSize = ConstUsize; +trait KnownSizeBaseSealed {} + +impl KnownSizeBaseSealed for [(); N] {} + +#[expect(private_bounds)] +pub trait KnownSizeBase: KnownSizeBaseSealed {} + +macro_rules! impl_known_size_base { + ($($size:literal),* $(,)?) => { + $(impl KnownSizeBase for [(); $size] {})* + }; +} + +#[rustfmt::skip] +impl_known_size_base! { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F, + 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, 0x018, 0x019, 0x01A, 0x01B, 0x01C, 0x01D, 0x01E, 0x01F, + 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x027, 0x028, 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, + 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F, + 0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047, 0x048, 0x049, 0x04A, 0x04B, 0x04C, 0x04D, 0x04E, 0x04F, + 0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, 0x058, 0x059, 0x05A, 0x05B, 0x05C, 0x05D, 0x05E, 0x05F, + 0x060, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, 0x068, 0x069, 0x06A, 0x06B, 0x06C, 0x06D, 0x06E, 0x06F, + 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, 0x078, 0x079, 0x07A, 0x07B, 0x07C, 0x07D, 0x07E, 0x07F, + 0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087, 0x088, 0x089, 0x08A, 0x08B, 0x08C, 0x08D, 0x08E, 0x08F, + 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097, 0x098, 0x099, 0x09A, 0x09B, 0x09C, 0x09D, 0x09E, 0x09F, + 0x0A0, 0x0A1, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0AA, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x0AF, + 0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0BA, 0x0BB, 0x0BC, 0x0BD, 0x0BE, 0x0BF, + 0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x0C7, 0x0C8, 0x0C9, 0x0CA, 0x0CB, 0x0CC, 0x0CD, 0x0CE, 0x0CF, + 0x0D0, 0x0D1, 0x0D2, 0x0D3, 0x0D4, 0x0D5, 0x0D6, 0x0D7, 0x0D8, 0x0D9, 0x0DA, 0x0DB, 0x0DC, 0x0DD, 0x0DE, 0x0DF, + 0x0E0, 0x0E1, 0x0E2, 0x0E3, 0x0E4, 0x0E5, 0x0E6, 0x0E7, 0x0E8, 0x0E9, 0x0EA, 0x0EB, 0x0EC, 0x0ED, 0x0EE, 0x0EF, + 0x0F0, 0x0F1, 0x0F2, 0x0F3, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x0F9, 0x0FA, 0x0FB, 0x0FC, 0x0FD, 0x0FE, 0x0FF, + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10A, 0x10B, 0x10C, 0x10D, 0x10E, 0x10F, + 0x110, 0x111, 0x112, 0x113, 0x114, 0x115, 0x116, 0x117, 0x118, 0x119, 0x11A, 0x11B, 0x11C, 0x11D, 0x11E, 0x11F, + 0x120, 0x121, 0x122, 0x123, 0x124, 0x125, 0x126, 0x127, 0x128, 0x129, 0x12A, 0x12B, 0x12C, 0x12D, 0x12E, 0x12F, + 0x130, 0x131, 0x132, 0x133, 0x134, 0x135, 0x136, 0x137, 0x138, 0x139, 0x13A, 0x13B, 0x13C, 0x13D, 0x13E, 0x13F, + 0x140, 0x141, 0x142, 0x143, 0x144, 0x145, 0x146, 0x147, 0x148, 0x149, 0x14A, 0x14B, 0x14C, 0x14D, 0x14E, 0x14F, + 0x150, 0x151, 0x152, 0x153, 0x154, 0x155, 0x156, 0x157, 0x158, 0x159, 0x15A, 0x15B, 0x15C, 0x15D, 0x15E, 0x15F, + 0x160, 0x161, 0x162, 0x163, 0x164, 0x165, 0x166, 0x167, 0x168, 0x169, 0x16A, 0x16B, 0x16C, 0x16D, 0x16E, 0x16F, + 0x170, 0x171, 0x172, 0x173, 0x174, 0x175, 0x176, 0x177, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x17F, + 0x180, 0x181, 0x182, 0x183, 0x184, 0x185, 0x186, 0x187, 0x188, 0x189, 0x18A, 0x18B, 0x18C, 0x18D, 0x18E, 0x18F, + 0x190, 0x191, 0x192, 0x193, 0x194, 0x195, 0x196, 0x197, 0x198, 0x199, 0x19A, 0x19B, 0x19C, 0x19D, 0x19E, 0x19F, + 0x1A0, 0x1A1, 0x1A2, 0x1A3, 0x1A4, 0x1A5, 0x1A6, 0x1A7, 0x1A8, 0x1A9, 0x1AA, 0x1AB, 0x1AC, 0x1AD, 0x1AE, 0x1AF, + 0x1B0, 0x1B1, 0x1B2, 0x1B3, 0x1B4, 0x1B5, 0x1B6, 0x1B7, 0x1B8, 0x1B9, 0x1BA, 0x1BB, 0x1BC, 0x1BD, 0x1BE, 0x1BF, + 0x1C0, 0x1C1, 0x1C2, 0x1C3, 0x1C4, 0x1C5, 0x1C6, 0x1C7, 0x1C8, 0x1C9, 0x1CA, 0x1CB, 0x1CC, 0x1CD, 0x1CE, 0x1CF, + 0x1D0, 0x1D1, 0x1D2, 0x1D3, 0x1D4, 0x1D5, 0x1D6, 0x1D7, 0x1D8, 0x1D9, 0x1DA, 0x1DB, 0x1DC, 0x1DD, 0x1DE, 0x1DF, + 0x1E0, 0x1E1, 0x1E2, 0x1E3, 0x1E4, 0x1E5, 0x1E6, 0x1E7, 0x1E8, 0x1E9, 0x1EA, 0x1EB, 0x1EC, 0x1ED, 0x1EE, 0x1EF, + 0x1F0, 0x1F1, 0x1F2, 0x1F3, 0x1F4, 0x1F5, 0x1F6, 0x1F7, 0x1F8, 0x1F9, 0x1FA, 0x1FB, 0x1FC, 0x1FD, 0x1FE, 0x1FF, + 0x200, +} + pub trait KnownSize: - GenericConstUsize + sealed::SizeTypeSealed + sealed::SizeSealed + Default + GenericConstUsize + + sealed::SizeTypeSealed + + sealed::SizeSealed + + Default + + FillInDefaultedGenerics { const SIZE: Self; type ArrayMatch: AsRef<[Expr]> @@ -89,35 +146,15 @@ pub trait KnownSize: + ToSimValueWithType>; } -macro_rules! known_widths { - ([] $($bits:literal)+) => { - impl KnownSize for ConstUsize<{ - let v = 0; - $(let v = v * 2 + $bits;)* - v - }> { - const SIZE: Self = Self; - type ArrayMatch = [Expr; Self::VALUE]; - type ArraySimValue = [SimValue; Self::VALUE]; - } - }; - ([2 $($rest:tt)*] $($bits:literal)+) => { - known_widths!([$($rest)*] $($bits)* 0); - known_widths!([$($rest)*] $($bits)* 1); - }; - ([2 $($rest:tt)*]) => { - known_widths!([$($rest)*] 0); - known_widths!([$($rest)*] 1); - impl KnownSize for ConstUsize<{2 $(* $rest)*}> { - const SIZE: Self = Self; - type ArrayMatch = [Expr; Self::VALUE]; - type ArraySimValue = [SimValue; Self::VALUE]; - } - }; +impl KnownSize for ConstUsize +where + [(); N]: KnownSizeBase, +{ + const SIZE: Self = Self; + type ArrayMatch = [Expr; N]; + type ArraySimValue = [SimValue; N]; } -known_widths!([2 2 2 2 2 2 2 2 2]); - pub trait SizeType: sealed::SizeTypeSealed + Copy @@ -129,6 +166,7 @@ pub trait SizeType: + 'static + Serialize + DeserializeOwned + + FillInDefaultedGenerics { type Size: Size; } @@ -530,6 +568,23 @@ fn deserialize_int_value<'de, D: Deserializer<'de>>( }) } +macro_rules! impl_valueless_op_forward { + ( + #[forward_to = $ForwardTrait:path] + impl<$($G:ident: $Bound:path),*> $TraitPath:ident$(::$TraitPathRest:ident)+<$($Rhs:ty)?> for $SelfTy:ty { + $($(type $Output:ident;)? + use $forward_fn:path as $fn:ident($self:ident $(, $rhs:ident)?);)? + } + ) => { + impl<$($G: $Bound),*> $TraitPath$(::$TraitPathRest)+<$($Rhs)?> for $SelfTy { + $($(type $Output = <$SelfTy as $ForwardTrait>::$Output;)? + fn $fn($self $(, $rhs: $Rhs)?) $(-> Self::$Output)? { + $forward_fn($self $(, $rhs)?) + })? + } + }; +} + macro_rules! impl_int { ($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => { #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -582,6 +637,176 @@ macro_rules! impl_int { } } + impl std::ops::Add>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + fn add(self, rhs: Valueless<$name>) -> Self::Output { + Valueless::new($name::new_dyn( + self.ty() + .width() + .max(rhs.ty().width()) + .checked_add(1) + .expect("width too big"), + )) + } + } + + impl_valueless_op_forward! { + #[forward_to = std::ops::Add>>] + impl std::ops::Sub>> + for Valueless<$name> + { + type Output; + + use std::ops::Add::>>::add as sub(self, rhs); + } + } + + impl std::ops::BitOr>> + for Valueless<$name> + { + type Output = Valueless; + + fn bitor(self, rhs: Valueless<$name>) -> Self::Output { + Valueless::new(UInt::new_dyn(self.ty().width().max(rhs.ty().width()))) + } + } + + impl_valueless_op_forward! { + #[forward_to = std::ops::BitOr>>] + impl std::ops::BitAnd>> + for Valueless<$name> + { + type Output; + + use std::ops::BitOr::>>::bitor as bitand(self, rhs); + } + } + + impl_valueless_op_forward! { + #[forward_to = std::ops::BitOr>>] + impl std::ops::BitXor>> + for Valueless<$name> + { + type Output; + + use std::ops::BitOr::>>::bitor as bitxor(self, rhs); + } + } + + impl std::ops::Not for Valueless<$name> { + type Output = Valueless>; + + fn not(self) -> Self::Output { + Valueless::new(self.ty().as_same_width_uint()) + } + } + + impl std::ops::Not for $value { + type Output = UIntValue; + + fn not(mut self) -> Self::Output { + // modifies in-place despite using `Not::not` + let _ = Not::not(self.bits_mut()); + UIntValue::new(self.into_bits()) + } + } + + impl std::ops::Not for &'_ $value { + type Output = UIntValue; + + fn not(self) -> Self::Output { + $value::not(self.clone()) + } + } + + impl std::ops::Mul>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + fn mul(self, rhs: Valueless<$name>) -> Self::Output { + Valueless::new($name::new_dyn( + self.ty() + .width() + .checked_add(rhs.ty().width()) + .expect("width too big"), + )) + } + } + + impl std::ops::Rem>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + fn rem(self, rhs: Valueless<$name>) -> Self::Output { + Valueless::new($name::new_dyn(self.ty().width().min(rhs.ty().width()))) + } + } + + impl std::ops::Shl>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + #[track_caller] + fn shl(self, rhs: Valueless>) -> Self::Output { + let Some(pow2_rhs_width) = rhs + .ty() + .width() + .try_into() + .ok() + .and_then(|v| 2usize.checked_pow(v)) + else { + panic!( + "dynamic left-shift amount's bit-width is too big, try casting the shift \ + amount to a smaller bit-width before shifting" + ); + }; + Valueless::new($name::new_dyn( + (pow2_rhs_width - 1) + .checked_add(self.ty().width()) + .expect("width too big"), + )) + } + } + + impl std::ops::Shr>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + fn shr(self, _rhs: Valueless>) -> Self::Output { + self + } + } + + impl std::ops::Shl for Valueless<$name> { + type Output = Valueless<$name>; + + fn shl(self, rhs: usize) -> Self::Output { + Valueless::new($name::new_dyn( + self.ty().width().checked_add(rhs).expect("width too big"), + )) + } + } + + impl std::ops::Shr for Valueless<$name> { + type Output = Valueless<$name>; + + fn shr(self, rhs: usize) -> Self::Output { + let width = self.ty().width().saturating_sub(rhs); + Valueless::new($name::new_dyn(if $SIGNED && width == 0 { + 1 + } else { + width + })) + } + } + impl sealed::BoolOrIntTypeSealed for $name {} impl BoolOrIntType for $name { @@ -843,6 +1068,12 @@ macro_rules! impl_int { } } + impl<'a, Width: Size> From<&'a $value> for BigInt { + fn from(v: &'a $value) -> BigInt { + v.to_bigint() + } + } + impl $value { pub fn width(&self) -> usize { if let Some(retval) = Width::KNOWN_VALUE { @@ -861,11 +1092,6 @@ macro_rules! impl_int { pub fn into_bits(self) -> Arc { self.bits } - pub fn ty(&self) -> $name { - $name { - width: Width::from_usize(self.width()), - } - } pub fn as_dyn_int(self) -> $value { $value { bits: self.bits, @@ -878,6 +1104,38 @@ macro_rules! impl_int { pub fn bits_mut(&mut self) -> &mut BitSlice { Arc::::make_mut(&mut self.bits) } + pub fn hdl_cmp(&self, other: &$value) -> std::cmp::Ordering { + self.to_bigint().cmp(&other.to_bigint()) + } + } + + impl ValueType for $value { + type Type = $name; + type ValueCategory = ValueCategoryValue; + + fn ty(&self) -> Self::Type { + $name { + width: Width::from_usize(self.width()), + } + } + } + + impl<'a, Width: Size> ToSimValueInner<'a> for $value { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Borrowed(this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(this) + } + } + + impl<'a, Width: Size> ToSimValueInner<'a> for &'a $value { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Borrowed(&**this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Borrowed(this) + } } impl ToLiteralBits for $value { @@ -1015,11 +1273,58 @@ impl SInt { } } -macro_rules! impl_prim_int { +impl std::ops::Div>> + for Valueless> +{ + type Output = Self; + + fn div(self, _rhs: Valueless>) -> Self::Output { + self + } +} + +impl std::ops::Div>> + for Valueless> +{ + type Output = Valueless; + + fn div(self, _rhs: Valueless>) -> Self::Output { + Valueless::new(SInt::new_dyn( + self.ty().width().checked_add(1).expect("width too big"), + )) + } +} + +impl std::ops::Neg for Valueless> { + type Output = Valueless; + fn neg(self) -> Self::Output { + Valueless::new(SInt::new_dyn( + self.ty().width().checked_add(1).expect("width too big"), + )) + } +} + +impl std::ops::Neg for SIntValue { + type Output = SIntValue; + fn neg(self) -> Self::Output { + -&self + } +} + +impl std::ops::Neg for &'_ SIntValue { + type Output = SIntValue; + fn neg(self) -> Self::Output { + let ty = self.to_valueless().neg().ty(); + ty.from_bigint_wrapping(&-self.to_bigint()) + } +} + +macro_rules! impl_prim_int_to_expr { ( $(#[$meta:meta])* $prim_int:ident, $ty:ty ) => { + $(#[$meta])* impl From<$prim_int> for <$ty as BoolOrIntType>::Value { fn from(v: $prim_int) -> Self { <$ty>::le_bytes_to_value_wrapping( @@ -1028,15 +1333,32 @@ macro_rules! impl_prim_int { ) } } + $(#[$meta])* impl From> for <$ty as BoolOrIntType>::Value { fn from(v: NonZero<$prim_int>) -> Self { v.get().into() } } $(#[$meta])* - impl ToExpr for $prim_int { + impl<'a> ToSimValueInner<'a> for $prim_int { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Self::into_sim_value_inner(*this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(this.into()) + } + } + $(#[$meta])* + impl ValueType for $prim_int { type Type = $ty; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + StaticType::TYPE + } + } + $(#[$meta])* + impl ToExpr for $prim_int { fn to_expr(&self) -> Expr { <$ty>::le_bytes_to_expr_wrapping( &self.to_le_bytes(), @@ -1045,9 +1367,25 @@ macro_rules! impl_prim_int { } } $(#[$meta])* - impl ToExpr for NonZero<$prim_int> { + impl<'a> ToSimValueInner<'a> for NonZero<$prim_int> { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Self::into_sim_value_inner(*this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(this.into()) + } + } + $(#[$meta])* + impl ValueType for NonZero<$prim_int> { type Type = $ty; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + StaticType::TYPE + } + } + $(#[$meta])* + impl ToExpr for NonZero<$prim_int> { fn to_expr(&self) -> Expr { self.get().to_expr() } @@ -1055,28 +1393,208 @@ macro_rules! impl_prim_int { }; } -impl_prim_int!(u8, UInt<8>); -impl_prim_int!(u16, UInt<16>); -impl_prim_int!(u32, UInt<32>); -impl_prim_int!(u64, UInt<64>); -impl_prim_int!(u128, UInt<128>); -impl_prim_int!(i8, SInt<8>); -impl_prim_int!(i16, SInt<16>); -impl_prim_int!(i32, SInt<32>); -impl_prim_int!(i64, SInt<64>); -impl_prim_int!(i128, SInt<128>); +macro_rules! impl_for_all_prim_uints { + ( + #[size =, $(rest:tt)*] + $m:ident!() + ) => { + impl_for_all_prim_uints!( + #[usize =, $($rest)*] + $m!() + ); + }; + ( + #[size = size, $(rest:tt)*] + $m:ident!() + ) => { + impl_for_all_prim_uints!( + #[usize = usize, $($rest)*] + $m!() + ); + }; + ( + #[usize = $($usize:ident)?, NonZero = $NonZero:ident] + $m:ident!() + ) => { + impl_for_all_prim_uints!( + #[usize = $($usize)?, NonZero = $NonZero, NonZero = $((NonZero<$usize>))?] + $m!() + ); + }; + ( + #[usize = $($usize:ident)?, NonZero =] + $m:ident!() + ) => { + impl_for_all_prim_uints!( + #[usize = $($usize)?, NonZero =, NonZero =] + $m!() + ); + }; + ( + #[usize = $($usize:ident)?, NonZero = $($NonZero:ident)?, NonZero = $(($($NonZeroUsize:tt)*))?] + $m:ident!() + ) => { + $m!(u8, UInt<8>); + $m!(u16, UInt<16>); + $m!(u32, UInt<32>); + $m!(u64, UInt<64>); + $m!(u128, UInt<128>); + $($m!($NonZero, UInt<8>);)? + $($m!($NonZero, UInt<16>);)? + $($m!($NonZero, UInt<32>);)? + $($m!($NonZero, UInt<64>);)? + $($m!($NonZero, UInt<128>);)? + $($m!( + /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt] + $usize, UInt<64> + );)? + $($m!( + /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt] + $($NonZeroUsize)*, UInt<64> + );)? + }; +} -impl_prim_int!( - /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt] - usize, UInt<64> +macro_rules! impl_for_all_prim_sints { + ( + #[size =, $(rest:tt)*] + $m:ident!() + ) => { + impl_for_all_prim_sints!( + #[isize =, $($rest)*] + $m!() + ); + }; + ( + #[size = size, $(rest:tt)*] + $m:ident!() + ) => { + impl_for_all_prim_sints!( + #[isize = isize, $($rest)*] + $m!() + ); + }; + ( + #[isize = $($isize:ident)?, NonZero = $NonZero:ident] + $m:ident!() + ) => { + impl_for_all_prim_sints!( + #[isize = $($isize)?, NonZero = $NonZero, NonZero = $((NonZero<$isize>))?] + $m!() + ); + }; + ( + #[isize = $($isize:ident)?, NonZero =] + $m:ident!() + ) => { + impl_for_all_prim_sints!( + #[isize = $($isize)?, NonZero =, NonZero =] + $m!() + ); + }; + ( + #[isize = $($isize:ident)?, NonZero = $($NonZero:ident)?, NonZero = $(($($NonZeroIsize:tt)*))?] + $m:ident!() + ) => { + $m!(i8, SInt<8>); + $m!(i16, SInt<16>); + $m!(i32, SInt<32>); + $m!(i64, SInt<64>); + $m!(i128, SInt<128>); + $($m!($NonZero, SInt<8>);)? + $($m!($NonZero, SInt<16>);)? + $($m!($NonZero, SInt<32>);)? + $($m!($NonZero, SInt<64>);)? + $($m!($NonZero, SInt<128>);)? + $($m!( + /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt] + $isize, SInt<64> + );)? + $($m!( + /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt] + $($NonZeroIsize)*, SInt<64> + );)? + }; +} + +impl_for_all_prim_uints!( + #[usize = usize, NonZero =] + impl_prim_int_to_expr!() ); -impl_prim_int!( - /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt] - isize, SInt<64> +impl_for_all_prim_sints!( + #[isize = isize, NonZero =] + impl_prim_int_to_expr!() ); -pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { +macro_rules! impl_prim_int_from_value { + ($prim_int:ident, UInt<$width:literal>) => { + impl_prim_int_from_value!($prim_int, UInt<$width>, UIntValue>); + }; + ($prim_int:ident, SInt<$width:literal>) => { + impl_prim_int_from_value!($prim_int, SInt<$width>, SIntValue>); + }; + ($prim_int:ident, $ty:ty, $value:ty) => { + impl From<$value> for $prim_int { + fn from(value: $value) -> Self { + value.as_int() + } + } + + impl<'a> From<&'a mut $value> for $prim_int { + fn from(value: &'a mut $value) -> Self { + value.as_int() + } + } + + impl From> for $prim_int { + fn from(value: SimValue<$ty>) -> Self { + value.as_int() + } + } + + impl<'a> From<&'a mut SimValue<$ty>> for $prim_int { + fn from(value: &'a mut SimValue<$ty>) -> Self { + value.as_int() + } + } + + impl<'a> From<&'a SimValue<$ty>> for $prim_int { + fn from(value: &'a SimValue<$ty>) -> Self { + value.as_int() + } + } + + impl<'a> From<&'a $value> for $prim_int { + fn from(value: &'a $value) -> Self { + value.as_int() + } + } + + impl $value { + pub fn as_int(&self) -> $prim_int { + let mut le_bytes = (0 as $prim_int).to_le_bytes(); + let bitslice = BitSlice::::from_slice_mut(&mut le_bytes); + bitslice.clone_from_bitslice(self.bits()); + $prim_int::from_le_bytes(le_bytes) + } + } + }; +} + +impl_for_all_prim_uints!( + #[usize =, NonZero =] + impl_prim_int_from_value!() +); + +impl_for_all_prim_sints!( + #[isize =, NonZero =] + impl_prim_int_from_value!() +); + +pub trait BoolOrIntType: + Type::Value> + sealed::BoolOrIntTypeSealed +{ type Width: Size; type Signed: GenericConstBool; type Value: Clone @@ -1301,12 +1819,80 @@ impl StaticType for Bool { const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } +impl From for UIntValue> { + fn from(value: bool) -> Self { + static VALUES: OnceLock<[UIntValue>; 2]> = OnceLock::new(); + let values = VALUES + .get_or_init(|| std::array::from_fn(|i| UInt::<1>::new_static().from_int_wrapping(i))); + values[value as usize].clone() + } +} + +impl From for SIntValue> { + fn from(value: bool) -> Self { + SIntValue::new(UIntValue::>::from(value).into_bits()) + } +} + +impl From for UIntValue { + fn from(value: bool) -> Self { + UIntValue::new(UIntValue::>::from(value).into_bits()) + } +} + +impl From for SIntValue { + fn from(value: bool) -> Self { + SIntValue::new(UIntValue::>::from(value).into_bits()) + } +} + impl ToLiteralBits for bool { fn to_literal_bits(&self) -> Result, NotALiteralExpr> { Ok(interned_bit(*self)) } } +impl<'a> ToSimValueInner<'a> for bool { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Owned(*this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(this) + } +} + +impl std::ops::Not for Valueless { + type Output = Valueless; + + fn not(self) -> Self::Output { + self + } +} + +impl std::ops::BitAnd for Valueless { + type Output = Valueless; + + fn bitand(self, _rhs: Valueless) -> Self::Output { + self + } +} + +impl std::ops::BitOr for Valueless { + type Output = Valueless; + + fn bitor(self, _rhs: Valueless) -> Self::Output { + self + } +} + +impl std::ops::BitXor for Valueless { + type Output = Valueless; + + fn bitxor(self, _rhs: Valueless) -> Self::Output { + self + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs index 5ddd38c0..acf2fecb 100644 --- a/crates/fayalite/src/int/uint_in_range.rs +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -4,13 +4,13 @@ use crate::{ bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder}, expr::{ - CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, - ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd}, + CastBitsTo, CastTo, CastToBits, CastToImpl, Expr, HdlPartialEq, HdlPartialEqImpl, + HdlPartialOrd, HdlPartialOrdImpl, }, - int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType}, - intern::{Intern, Interned}, + int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType, UIntValue}, + intern::{Intern, InternSlice, Interned}, phantom_const::PhantomConst, - sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType}, + sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, @@ -22,7 +22,7 @@ use serde::{ Deserialize, Deserializer, Serialize, Serializer, de::{Error, Visitor, value::UsizeDeserializer}, }; -use std::{fmt, marker::PhantomData, ops::Index}; +use std::{borrow::Cow, fmt, marker::PhantomData, ops::Index}; const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"]; @@ -96,7 +96,6 @@ impl Type for UIntInRangeMaskType { 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; @@ -112,8 +111,8 @@ impl BundleType for UIntInRangeMaskType { flipped: false, ty: range.canonical(), }, - ][..] - .intern() + ] + .intern_slice() } } @@ -136,30 +135,57 @@ impl ToSimValueWithType for bool { } } -impl ExprCastTo for UIntInRangeMaskType { - fn cast_to(src: Expr, to_type: Bool) -> Expr { - src.cast_to_bits().cast_to(to_type) +impl CastToImpl for UIntInRangeMaskType { + type ValueOutput = bool; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: Bool, + ) -> Self::ValueOutput { + *value + } + + fn cast_expr_to(value: Expr, to_type: Bool) -> Expr { + value.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 CastToImpl for Bool { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: UIntInRangeMaskType, + ) -> Self::ValueOutput { + SimValue::from_value(to_type, *value) + } + + fn cast_expr_to(value: Expr, to_type: UIntInRangeMaskType) -> Expr { + value.cast_to_static::>().cast_bits_to(to_type) } } -impl ExprPartialEq for UIntInRangeMaskType { - fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { +impl HdlPartialEqImpl for UIntInRangeMaskType { + #[track_caller] + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, Self::SimValue>, + ) -> bool { + *lhs_value == *rhs_value + } + + #[track_caller] + fn cmp_expr_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 + #[track_caller] + fn cmp_expr_ne(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) } } @@ -300,9 +326,16 @@ macro_rules! define_uint_in_range_type { } } pub fn new(start: Start::SizeType, end: End::SizeType) -> Self { - Self::from_phantom_const_range(PhantomConst::new( - $SerdeRange { start, end }.intern_sized(), - )) + Self::from_phantom_const_range(PhantomConst::new_sized($SerdeRange { start, end })) + } + pub fn bit_width(self) -> usize { + self.value.width() + } + pub fn start(self) -> Start::SizeType { + self.range.get().start + } + pub fn end(self) -> End::SizeType { + self.range.get().end } } @@ -393,7 +426,6 @@ macro_rules! define_uint_in_range_type { 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; @@ -409,8 +441,8 @@ macro_rules! define_uint_in_range_type { flipped: false, ty: range.canonical(), }, - ][..] - .intern() + ] + .intern_slice() } } @@ -477,32 +509,64 @@ macro_rules! define_uint_in_range_type { } } - impl ExprCastTo for $UIntInRangeType { - fn cast_to(src: Expr, to_type: UInt) -> Expr { - src.cast_to_bits().cast_to(to_type) + impl CastToImpl> + for $UIntInRangeType + { + type ValueOutput = UIntValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: UIntType, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: UIntType) -> Expr> { + value.cast_to_bits().cast_to(to_type) } } - impl ExprCastTo<$UIntInRangeType> for UInt { - fn cast_to( - src: Expr, + impl CastToImpl<$UIntInRangeType> + for UIntType + { + type ValueOutput = SimValue<$UIntInRangeType>; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: $UIntInRangeType, + ) -> Self::ValueOutput { + value.cast_to(to_type.value).cast_bits_to(to_type) + } + + fn cast_expr_to( + value: Expr, to_type: $UIntInRangeType, ) -> Expr<$UIntInRangeType> { - src.cast_bits_to(to_type) + value.cast_to(to_type.value).cast_bits_to(to_type) } } impl - ExprPartialEq<$UIntInRangeType> + HdlPartialEqImpl<$UIntInRangeType> for $UIntInRangeType { - fn cmp_eq( + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + *lhs_value == *rhs_value + } + fn cmp_expr_eq( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) } - fn cmp_ne( + fn cmp_expr_ne( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { @@ -511,28 +575,60 @@ macro_rules! define_uint_in_range_type { } impl - ExprPartialOrd<$UIntInRangeType> + HdlPartialOrdImpl<$UIntInRangeType> for $UIntInRangeType { - fn cmp_lt( + fn cmp_value_lt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + PartialOrd::lt(&*lhs_value, &*rhs_value) + } + fn cmp_value_le( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + PartialOrd::le(&*lhs_value, &*rhs_value) + } + fn cmp_value_gt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + PartialOrd::gt(&*lhs_value, &*rhs_value) + } + fn cmp_value_ge( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + PartialOrd::ge(&*lhs_value, &*rhs_value) + } + fn cmp_expr_lt( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits()) } - fn cmp_le( + fn cmp_expr_le( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { lhs.cast_to_bits().cmp_le(rhs.cast_to_bits()) } - fn cmp_gt( + fn cmp_expr_gt( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits()) } - fn cmp_ge( + fn cmp_expr_ge( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { @@ -540,70 +636,138 @@ macro_rules! define_uint_in_range_type { } } - impl - SimValuePartialEq<$UIntInRangeType> - for $UIntInRangeType - { - fn sim_value_eq( - this: &SimValue, - other: &SimValue<$UIntInRangeType>, - ) -> bool { - **this == **other - } - } - - impl ExprPartialEq> + impl HdlPartialEqImpl> for $UIntInRangeType { - fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialEq::cmp_eq(&*lhs_value, &*rhs_value) + } + fn cmp_expr_eq(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_eq(rhs) } - fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_ne(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_ne(rhs) } } - impl ExprPartialEq<$UIntInRangeType> + impl HdlPartialEqImpl<$UIntInRangeType> for UIntType { - fn cmp_eq(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialEq::cmp_eq(&*lhs_value, *rhs_value) + } + fn cmp_expr_eq(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_eq(rhs.cast_to_bits()) } - fn cmp_ne(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_expr_ne(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_ne(rhs.cast_to_bits()) } } - impl ExprPartialOrd> + impl HdlPartialOrdImpl> for $UIntInRangeType { - fn cmp_lt(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_value_lt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialOrd::cmp_lt(&*lhs_value, &*rhs_value) + } + fn cmp_value_le( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialOrd::cmp_le(&*lhs_value, &*rhs_value) + } + fn cmp_value_gt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialOrd::cmp_gt(&*lhs_value, &*rhs_value) + } + fn cmp_value_ge( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialOrd::cmp_ge(&*lhs_value, &*rhs_value) + } + fn cmp_expr_lt(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_lt(rhs) } - fn cmp_le(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_le(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_le(rhs) } - fn cmp_gt(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_gt(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_gt(rhs) } - fn cmp_ge(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_ge(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_ge(rhs) } } - impl ExprPartialOrd<$UIntInRangeType> + impl HdlPartialOrdImpl<$UIntInRangeType> for UIntType { - fn cmp_lt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_value_lt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialOrd::cmp_lt(&*lhs_value, *rhs_value) + } + fn cmp_value_le( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialOrd::cmp_le(&*lhs_value, *rhs_value) + } + fn cmp_value_gt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialOrd::cmp_gt(&*lhs_value, *rhs_value) + } + fn cmp_value_ge( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialOrd::cmp_ge(&*lhs_value, *rhs_value) + } + fn cmp_expr_lt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_lt(rhs.cast_to_bits()) } - fn cmp_le(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_expr_le(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_le(rhs.cast_to_bits()) } - fn cmp_gt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_expr_gt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_gt(rhs.cast_to_bits()) } - fn cmp_ge(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_expr_ge(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_ge(rhs.cast_to_bits()) } } diff --git a/crates/fayalite/src/intern.rs b/crates/fayalite/src/intern.rs index af91f0a8..b68140b5 100644 --- a/crates/fayalite/src/intern.rs +++ b/crates/fayalite/src/intern.rs @@ -9,11 +9,13 @@ use std::{ any::{Any, TypeId}, borrow::{Borrow, Cow}, cmp::Ordering, + ffi::{OsStr, OsString}, fmt, hash::{BuildHasher, Hash, Hasher}, iter::FusedIterator, marker::PhantomData, ops::Deref, + path::{Path, PathBuf}, sync::{Mutex, RwLock}, }; @@ -287,15 +289,266 @@ impl InternedCompare for BitSlice { } } -impl InternedCompare for str { - type InternedCompareKey = PtrEqWithMetadata; - fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { - PtrEqWithMetadata(this) +/// Safety: `as_bytes` and `from_bytes_unchecked` must return the same pointer as the input. +/// all values returned by `as_bytes` must be valid to pass to `from_bytes_unchecked`. +/// `into_bytes` must return the exact same thing as `as_bytes`. +/// `Interned` must contain the exact same references as `Interned<[u8]>`, +/// so they can be safely interconverted without needing re-interning. +unsafe trait InternStrLike: ToOwned { + fn as_bytes(this: &Self) -> &[u8]; + fn into_bytes(this: Self::Owned) -> Vec; + /// Safety: `bytes` must be a valid sequence of bytes for this type. All UTF-8 sequences are valid. + unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self; +} + +macro_rules! impl_intern_str_like { + ($ty:ty, owned = $Owned:ty) => { + impl InternedCompare for $ty { + type InternedCompareKey = PtrEqWithMetadata<[u8]>; + fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { + PtrEqWithMetadata(InternStrLike::as_bytes(this)) + } + } + impl Intern for $ty { + fn intern(&self) -> Interned { + Self::intern_cow(Cow::Borrowed(self)) + } + fn intern_cow(this: Cow<'_, Self>) -> Interned { + Interned::cast_unchecked( + <[u8]>::intern_cow(match this { + Cow::Borrowed(v) => Cow::Borrowed(::as_bytes(v)), + Cow::Owned(v) => { + // verify $Owned is correct + let v: $Owned = v; + Cow::Owned(::into_bytes(v)) + } + }), + // Safety: guaranteed safe because we got the bytes from `as_bytes`/`into_bytes` + |v| unsafe { ::from_bytes_unchecked(v) }, + ) + } + } + impl Default for Interned<$ty> { + fn default() -> Self { + // Safety: safe because the empty sequence is valid UTF-8 + unsafe { <$ty as InternStrLike>::from_bytes_unchecked(&[]) }.intern() + } + } + impl<'de> Deserialize<'de> for Interned<$ty> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Cow::<'de, $ty>::deserialize(deserializer).map(Intern::intern_cow) + } + } + impl From<$Owned> for Interned<$ty> { + fn from(v: $Owned) -> Self { + v.intern_deref() + } + } + impl From> for $Owned { + fn from(v: Interned<$ty>) -> Self { + Interned::into_inner(v).into() + } + } + impl From> for Box<$ty> { + fn from(v: Interned<$ty>) -> Self { + Interned::into_inner(v).into() + } + } + }; +} + +// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `str` +unsafe impl InternStrLike for str { + fn as_bytes(this: &Self) -> &[u8] { + this.as_bytes() + } + fn into_bytes(this: Self::Owned) -> Vec { + this.into_bytes() + } + unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { + // Safety: `bytes` is guaranteed UTF-8 by the caller + unsafe { str::from_utf8_unchecked(bytes) } + } +} + +impl_intern_str_like!(str, owned = String); + +// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `OsStr` +unsafe impl InternStrLike for OsStr { + fn as_bytes(this: &Self) -> &[u8] { + this.as_encoded_bytes() + } + fn into_bytes(this: Self::Owned) -> Vec { + this.into_encoded_bytes() + } + unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { + // Safety: `bytes` is guaranteed valid for `OsStr` by the caller + unsafe { OsStr::from_encoded_bytes_unchecked(bytes) } + } +} + +impl_intern_str_like!(OsStr, owned = OsString); + +// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `OsStr` +unsafe impl InternStrLike for Path { + fn as_bytes(this: &Self) -> &[u8] { + this.as_os_str().as_encoded_bytes() + } + fn into_bytes(this: Self::Owned) -> Vec { + this.into_os_string().into_encoded_bytes() + } + unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { + // Safety: `bytes` is guaranteed valid for `OsStr` by the caller + unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(bytes)) } + } +} + +impl_intern_str_like!(Path, owned = PathBuf); + +impl Interned { + pub fn from_utf8(v: Interned<[u8]>) -> Result { + Interned::try_cast_unchecked(v, str::from_utf8) + } + pub fn as_interned_bytes(self) -> Interned<[u8]> { + Interned::cast_unchecked(self, str::as_bytes) + } + pub fn as_interned_os_str(self) -> Interned { + Interned::cast_unchecked(self, AsRef::as_ref) + } + pub fn as_interned_path(self) -> Interned { + Interned::cast_unchecked(self, AsRef::as_ref) + } +} + +impl From> for Interned { + fn from(value: Interned) -> Self { + value.as_interned_os_str() + } +} + +impl From> for Interned { + fn from(value: Interned) -> Self { + value.as_interned_path() + } +} + +impl Interned { + pub fn as_interned_encoded_bytes(self) -> Interned<[u8]> { + Interned::cast_unchecked(self, OsStr::as_encoded_bytes) + } + pub fn to_interned_str(self) -> Option> { + Interned::try_cast_unchecked(self, |v| v.to_str().ok_or(())).ok() + } + pub fn display(self) -> std::ffi::os_str::Display<'static> { + Self::into_inner(self).display() + } + pub fn as_interned_path(self) -> Interned { + Interned::cast_unchecked(self, AsRef::as_ref) + } +} + +impl From> for Interned { + fn from(value: Interned) -> Self { + value.as_interned_path() + } +} + +impl Interned { + pub fn as_interned_os_str(self) -> Interned { + Interned::cast_unchecked(self, AsRef::as_ref) + } + pub fn to_interned_str(self) -> Option> { + Interned::try_cast_unchecked(self, |v| v.to_str().ok_or(())).ok() + } + pub fn display(self) -> std::path::Display<'static> { + Self::into_inner(self).display() + } + pub fn interned_file_name(self) -> Option> { + Some(self.file_name()?.intern()) + } +} + +impl From> for Interned { + fn from(value: Interned) -> Self { + value.as_interned_os_str() + } +} + +pub trait InternSlice: Sized { + type Element: 'static + Send + Sync + Clone + Hash + Eq; + fn intern_slice(self) -> Interned<[Self::Element]>; +} + +impl InternSlice for Box<[T]> { + type Element = T; + fn intern_slice(self) -> Interned<[Self::Element]> { + self.into_vec().intern_slice() + } +} + +impl InternSlice for Vec { + type Element = T; + fn intern_slice(self) -> Interned<[Self::Element]> { + self.intern_deref() + } +} + +impl InternSlice for &'_ [T] { + type Element = T; + fn intern_slice(self) -> Interned<[Self::Element]> { + self.intern() + } +} + +impl InternSlice for &'_ mut [T] { + type Element = T; + fn intern_slice(self) -> Interned<[Self::Element]> { + self.intern() + } +} + +impl InternSlice for [T; N] { + type Element = T; + fn intern_slice(self) -> Interned<[Self::Element]> { + (&self).intern_slice() + } +} + +impl InternSlice for Box<[T; N]> { + type Element = T; + fn intern_slice(self) -> Interned<[Self::Element]> { + let this: Box<[T]> = self; + this.intern_slice() + } +} + +impl InternSlice for &'_ [T; N] { + type Element = T; + fn intern_slice(self) -> Interned<[Self::Element]> { + let this: &[T] = self; + this.intern() + } +} + +impl InternSlice for &'_ mut [T; N] { + type Element = T; + fn intern_slice(self) -> Interned<[Self::Element]> { + let this: &[T] = self; + this.intern() } } pub trait Intern: Any + Send + Sync { fn intern(&self) -> Interned; + fn intern_deref(self) -> Interned + where + Self: Sized + Deref>, + { + Self::Target::intern_owned(self) + } fn intern_sized(self) -> Interned where Self: Clone, @@ -316,6 +569,30 @@ pub trait Intern: Any + Send + Sync { } } +impl From> for Interned { + fn from(value: Cow<'_, T>) -> Self { + Intern::intern_cow(value) + } +} + +impl From<&'_ T> for Interned { + fn from(value: &'_ T) -> Self { + Intern::intern(value) + } +} + +impl From for Interned { + fn from(value: T) -> Self { + Intern::intern_sized(value) + } +} + +impl From> for Cow<'_, T> { + fn from(value: Interned) -> Self { + Cow::Borrowed(Interned::into_inner(value)) + } +} + struct InternerState { table: HashTable<&'static T>, hasher: DefaultBuildHasher, @@ -381,12 +658,6 @@ impl Interner { } } -impl Interner { - fn intern_str(&self, value: Cow<'_, str>) -> Interned { - self.intern(|value| value.into_owned().leak(), value) - } -} - pub struct Interned { inner: &'static T, } @@ -416,6 +687,12 @@ forward_fmt_trait!(Pointer); forward_fmt_trait!(UpperExp); forward_fmt_trait!(UpperHex); +impl, U: ?Sized> AsRef for Interned { + fn as_ref(&self) -> &U { + T::as_ref(self) + } +} + #[derive(Clone, Debug)] pub struct InternedSliceIter { slice: Interned<[T]>, @@ -485,6 +762,57 @@ where } } +impl FromIterator for Interned +where + String: FromIterator, +{ + fn from_iter>(iter: T) -> Self { + String::from_iter(iter).intern_deref() + } +} + +impl FromIterator for Interned +where + PathBuf: FromIterator, +{ + fn from_iter>(iter: T) -> Self { + PathBuf::from_iter(iter).intern_deref() + } +} + +impl FromIterator for Interned +where + OsString: FromIterator, +{ + fn from_iter>(iter: T) -> Self { + OsString::from_iter(iter).intern_deref() + } +} + +impl From> for clap::builder::Str { + fn from(value: Interned) -> Self { + Interned::into_inner(value).into() + } +} + +impl From> for clap::builder::OsStr { + fn from(value: Interned) -> Self { + Interned::into_inner(value).into() + } +} + +impl From> for clap::builder::StyledStr { + fn from(value: Interned) -> Self { + Interned::into_inner(value).into() + } +} + +impl From> for clap::Id { + fn from(value: Interned) -> Self { + Interned::into_inner(value).into() + } +} + impl From> for Vec { fn from(value: Interned<[T]>) -> Self { Vec::from(&*value) @@ -497,24 +825,12 @@ impl From> for Box<[T]> { } } -impl From> for String { - fn from(value: Interned) -> Self { - String::from(&*value) - } -} - impl Default for Interned<[I]> where [I]: Intern, { fn default() -> Self { - [][..].intern() - } -} - -impl Default for Interned { - fn default() -> Self { - "".intern() + Intern::intern(&[]) } } @@ -645,15 +961,6 @@ impl<'de> Deserialize<'de> for Interned { } } -impl<'de> Deserialize<'de> for Interned { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - String::deserialize(deserializer).map(Intern::intern_owned) - } -} - impl Intern for T { fn intern(&self) -> Interned { Self::intern_cow(Cow::Borrowed(self)) @@ -714,26 +1021,6 @@ impl Intern for BitSlice { } } -impl Intern for str { - fn intern(&self) -> Interned { - Self::intern_cow(Cow::Borrowed(self)) - } - - fn intern_owned(this: ::Owned) -> Interned - where - Self: ToOwned, - { - Self::intern_cow(Cow::Owned(this)) - } - - fn intern_cow(this: Cow<'_, Self>) -> Interned - where - Self: ToOwned, - { - Interner::get().intern_str(this) - } -} - pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy { type InputRef<'a>: 'a + Send + Sync + Hash + Copy; type InputOwned: 'static + Send + Sync; diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 932464ba..156aeed2 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -4,6 +4,18 @@ // TODO: enable: // #![warn(missing_docs)] +#![deny( + rustdoc::bare_urls, + rustdoc::broken_intra_doc_links, + rustdoc::invalid_codeblock_attributes, + rustdoc::invalid_html_tags, + rustdoc::invalid_rust_codeblocks, + rustdoc::private_doc_tests, + rustdoc::private_intra_doc_links, + rustdoc::redundant_explicit_links, + rustdoc::unescaped_backticks +)] + //! [Main Documentation][_docs] extern crate self as fayalite; @@ -74,6 +86,135 @@ macro_rules! __cfg_expansion_helper { pub use fayalite_proc_macros::hdl_module; #[doc(inline)] +/// The `#[hdl]` attribute is supported on several different kinds of [Rust Items](https://doc.rust-lang.org/reference/items.html): +/// +/// # Functions and Methods +/// Enable's the stuff that you can use inside a [module's body](crate::_docs::modules::module_bodies), +/// but without being a module or changing the function's signature. +/// The only exception is that you can't use stuff that requires the automatically-provided `m` variable. +/// +/// # Structs +// TODO: expand on struct docs +/// e.g.: +/// ``` +/// # use fayalite::prelude::*; +/// # #[hdl] +/// # pub struct OtherStruct {} +/// #[hdl] +/// pub struct MyStruct { +/// #[hdl(flip)] +/// pub a: UInt<5>, +/// pub b: Bool, +/// #[hdl(flip)] +/// pub c: OtherStruct, +/// } +/// ``` +/// +/// # Enums +// TODO: expand on enum docs +/// e.g.: +/// ``` +/// # use fayalite::prelude::*; +/// # #[hdl] +/// # pub struct MyStruct {} +/// #[hdl] +/// pub enum MyEnum { +/// A(UInt<3>), +/// B, +/// C(MyStruct), +/// } +/// ``` +/// +/// # Type Aliases +/// +/// There's three different ways you can create a type alias: +/// +/// # Normal Type Alias +/// +/// This works exactly how you'd expect: +/// ``` +/// # use fayalite::prelude::*; +/// # #[hdl] +/// # pub struct MyStruct { +/// # v: T, +/// # } +/// #[hdl] +/// pub type MyType = MyStruct; +/// +/// // you can then use Fayalite's standard syntax for creating dynamic types at runtime: +/// +/// let ty = MyType[UInt[3]]; +/// assert_eq!(ty, MyStruct[UInt[3]]); +/// ``` +/// +/// # Type Alias that gets a [`Type`] from a [`PhantomConst`] +/// +/// This allows you to use some computed property of a [`PhantomConst`] to get a [`Type`] that you can use in other #[hdl] types. +/// +/// ``` +/// # use fayalite::prelude::*; +/// #[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)] +/// pub struct Config { +/// pub foo: usize, +/// pub bar: Bundle, +/// } +/// +/// // the expression inside `get` is called with `Interned` and returns `Array` +/// #[hdl(get(|config| Array[config.bar][config.foo]))] +/// pub type GetMyArray> = Array; +/// +/// // you can then use it in other types: +/// +/// #[hdl(no_static)] +/// pub struct WrapMyArray> { +/// pub my_array: GetMyArray

, +/// } +/// +/// // you can then use Fayalite's standard syntax for creating dynamic types at runtime: +/// let bar = Bundle::new(Default::default()); +/// let config = PhantomConst::new_sized(Config { foo: 12, bar }); +/// let ty = WrapMyArray[config]; +/// assert_eq!(ty.my_array, Array[bar][12]); +/// ``` +/// +/// # Type Alias that gets a [`Size`] from a [`PhantomConst`] +/// +/// This allows you to use some computed property of a [`PhantomConst`] to get a [`Size`] that you can use in other #[hdl] types. +/// +/// ``` +/// # use fayalite::prelude::*; +/// # #[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)] +/// # pub struct ConfigItem {} +/// # impl ConfigItem { +/// # pub fn new() -> Self { +/// # Self {} +/// # } +/// # } +/// #[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)] +/// pub struct Config { +/// pub items: Vec, +/// } +/// +/// // the expression inside `get` is called with `Interned` and returns `usize` (not DynSize) +/// #[hdl(get(|config| config.items.len()))] +/// pub type GetItemsLen> = DynSize; // must be DynSize +/// +/// // you can then use it in other types: +/// +/// #[hdl(no_static)] +/// pub struct FlagPerItem> { +/// pub flags: ArrayType>, +/// } +/// +/// // you can then use Fayalite's standard syntax for creating dynamic types at runtime: +/// let config = PhantomConst::new_sized(Config { items: vec![ConfigItem::new(); 5] }); +/// let ty = FlagPerItem[config]; +/// assert_eq!(ty.flags, Array[Bool][5]); +/// ``` +/// +/// [`PhantomConst`]: crate::phantom_const::PhantomConst +/// [`Size`]: crate::int::Size +/// [`Type`]: crate::ty::Type pub use fayalite_proc_macros::hdl; pub use bitvec; @@ -87,8 +228,8 @@ pub mod _docs; pub mod annotations; pub mod array; +pub mod build; pub mod bundle; -pub mod cli; pub mod clock; pub mod enum_; pub mod expr; @@ -99,6 +240,7 @@ pub mod intern; pub mod memory; pub mod module; pub mod phantom_const; +pub mod platform; pub mod prelude; pub mod reg; pub mod reset; @@ -107,4 +249,5 @@ pub mod source_location; pub mod testing; pub mod ty; pub mod util; +pub mod vendor; pub mod wire; diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index 46eb59bd..83e74376 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -7,7 +7,10 @@ use crate::{ array::{Array, ArrayType}, bundle::{Bundle, BundleType}, clock::Clock, - expr::{Expr, Flow, ToExpr, ToLiteralBits, ops::BundleLiteral, repeat}, + expr::{ + Expr, Flow, ToExpr, ToLiteralBits, ValueType, ops::BundleLiteral, repeat, + value_category::ValueCategoryExpr, + }, hdl, int::{Bool, DynSize, Size, UInt, UIntType}, intern::{Intern, Interned}, @@ -366,10 +369,16 @@ impl fmt::Debug for MemPort { } } -impl MemPort { - pub fn ty(&self) -> T::Port { +impl ValueType for MemPort { + type Type = T::Port; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> T::Port { T::port_ty(self) } +} + +impl MemPort { pub fn source_location(&self) -> SourceLocation { self.source_location } @@ -830,7 +839,7 @@ impl MemBuilder { depth: Option, initial_value: Expr, ) -> Interned { - let initial_value_ty = Expr::ty(initial_value); + let initial_value_ty = initial_value.ty(); assert_eq!( *mem_element_type, Element::from_canonical(initial_value_ty.element()), @@ -1011,7 +1020,7 @@ impl MemBuilder { target.depth, initial_value, )); - target.depth = Some(Expr::ty(initial_value).len()); + target.depth = Some(initial_value.ty().len()); } #[track_caller] pub fn initial_value_bit_slice(&mut self, initial_value: Interned) { diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index a81893d3..9d1a0e76 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -8,17 +8,19 @@ use crate::{ clock::{Clock, ClockDomain}, enum_::{Enum, EnumMatchVariantsIter, EnumType}, expr::{ - Expr, Flow, ToExpr, + Expr, Flow, ToExpr, ValueType, ops::VariantAccess, target::{ GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, + value_category::ValueCategoryExpr, }, formal::FormalKind, int::{Bool, DynSize, Size}, intern::{Intern, Interned}, memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, + platform::PlatformIOBuilder, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, sim::{ExternModuleSimGenerator, ExternModuleSimulation}, @@ -40,7 +42,6 @@ use std::{ marker::PhantomData, mem, num::NonZeroU64, - ops::Deref, rc::Rc, sync::atomic::AtomicU64, }; @@ -66,6 +67,8 @@ pub trait ModuleBuildingStatus: type ModuleBody: fmt::Debug; type StmtAnnotations: 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug; type ModuleIOAnnotations; + type ExternModuleParameters: fmt::Debug; + type ExternModuleClocksForPast: fmt::Debug; } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)] @@ -78,6 +81,8 @@ impl ModuleBuildingStatus for ModuleBuilt { type ModuleBody = Block; type StmtAnnotations = Interned<[TargetedAnnotation]>; type ModuleIOAnnotations = Interned<[TargetedAnnotation]>; + type ExternModuleParameters = Interned<[ExternModuleParameter]>; + type ExternModuleClocksForPast = Interned<[Target]>; } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)] @@ -90,6 +95,8 @@ impl ModuleBuildingStatus for ModuleBuilding { type ModuleBody = BuilderModuleBody; type StmtAnnotations = (); type ModuleIOAnnotations = Vec; + type ExternModuleParameters = Vec; + type ExternModuleClocksForPast = Vec; } #[derive(Debug)] @@ -199,7 +206,7 @@ impl StmtConnect { source_location, } = *self; assert!( - Expr::ty(lhs).can_connect(Expr::ty(rhs)), + lhs.ty().can_connect(rhs.ty()), "can't connect types that are not equivalent:\nlhs type:\n{lhs_orig_ty:?}\nrhs type:\n{rhs_orig_ty:?}\nat: {source_location}", ); assert!( @@ -213,14 +220,14 @@ impl StmtConnect { match Expr::flow(rhs) { Flow::Source | Flow::Duplex => {} Flow::Sink => assert!( - Expr::ty(rhs).is_passive(), + rhs.ty().is_passive(), "can't connect from sink with non-passive type\nat: {source_location}" ), } } #[track_caller] fn assert_validity(&self) { - self.assert_validity_with_original_types(Expr::ty(self.lhs), Expr::ty(self.rhs)); + self.assert_validity_with_original_types(self.lhs.ty(), self.rhs.ty()); } } @@ -325,7 +332,7 @@ impl Copy for StmtMatch {} impl StmtMatch { #[track_caller] fn assert_validity(&self) { - assert_eq!(Expr::ty(self.expr).variants().len(), self.blocks.len()); + assert_eq!(self.expr.ty().variants().len(), self.blocks.len()); } } @@ -759,6 +766,15 @@ impl fmt::Debug for Instance { } } +impl ValueType for Instance { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> T { + self.instantiated.io_ty() + } +} + impl Instance { pub fn canonical(self) -> Instance { let Self { @@ -822,9 +838,6 @@ impl Instance { pub fn must_connect_to(&self) -> bool { true } - pub fn ty(&self) -> T { - self.instantiated.io_ty() - } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -833,6 +846,8 @@ pub struct AnnotatedModuleIO { pub module_io: ModuleIO, } +impl Copy for AnnotatedModuleIO {} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum ModuleKind { Extern, @@ -1077,26 +1092,65 @@ impl From for ModuleBody { } } +#[track_caller] +fn validate_clock_for_past( + clock_for_past: Option, + module_io: &[AnnotatedModuleIO], +) -> Target { + if let Some(clock_for_past) = clock_for_past { + assert_eq!( + clock_for_past.canonical_ty(), + Clock.canonical(), + "clock_for_past: clock is not of type Clock", + ); + if clock_for_past + .base() + .module_io() + .is_some_and(|v| module_io.iter().any(|module_io| module_io.module_io == *v)) + { + let mut target = clock_for_past; + while let Target::Child(child) = target { + match *child.path_element() { + TargetPathElement::BundleField(_) | TargetPathElement::ArrayElement(_) => {} + TargetPathElement::DynArrayElement(_) => { + panic!( + "clock_for_past: clock must be a static target (you can't use `Expr` array indexes):\n{clock_for_past:?}" + ); + } + } + target = *child.parent(); + } + return clock_for_past; + } + } + panic!("clock_for_past: clock must be some part of this module's I/O:\n{clock_for_past:?}"); +} + #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] -pub struct ExternModuleBody< - P: Deref = Interned<[ExternModuleParameter]>, -> { +pub struct ExternModuleBody { pub verilog_name: Interned, - pub parameters: P, + pub parameters: S::ExternModuleParameters, + /// [`Clock`]s that the [`Simulation`] will store the past values of all [`ModuleIO`] for. + /// + /// [`Simulation`]: crate::sim::Simulation + pub clocks_for_past: S::ExternModuleClocksForPast, pub simulation: Option, } -impl From>> for ExternModuleBody { - fn from(value: ExternModuleBody>) -> Self { +impl From> for ExternModuleBody { + fn from(value: ExternModuleBody) -> Self { let ExternModuleBody { verilog_name, parameters, + clocks_for_past, simulation, } = value; let parameters = Intern::intern_owned(parameters); + let clocks_for_past = Intern::intern_owned(clocks_for_past); Self { verilog_name, parameters, + clocks_for_past, simulation, } } @@ -1109,15 +1163,12 @@ impl From for ModuleBody { } #[derive(Debug)] -pub enum ModuleBody< - S: ModuleBuildingStatus = ModuleBuilt, - P: Deref = Interned<[ExternModuleParameter]>, -> { +pub enum ModuleBody { Normal(NormalModuleBody), - Extern(ExternModuleBody

), + Extern(ExternModuleBody), } -pub(crate) type ModuleBodyBuilding = ModuleBody>; +pub(crate) type ModuleBodyBuilding = ModuleBody; impl ModuleBodyBuilding { pub(crate) fn builder_normal_body_opt( @@ -1138,9 +1189,7 @@ impl ModuleBodyBuilding { } } #[track_caller] - pub(crate) fn builder_extern_body( - &mut self, - ) -> &mut ExternModuleBody> { + pub(crate) fn builder_extern_body(&mut self) -> &mut ExternModuleBody { if let Self::Extern(v) = self { v } else { @@ -1212,6 +1261,12 @@ pub struct Module { module_annotations: Interned<[Annotation]>, } +impl AsRef for Module { + fn as_ref(&self) -> &Self { + self + } +} + #[derive(Default)] struct DebugFmtModulesState { seen: HashSet, @@ -1288,11 +1343,13 @@ impl fmt::Debug for DebugModuleBody { ModuleBody::Extern(ExternModuleBody { verilog_name, parameters, + clocks_for_past, simulation, }) => { debug_struct .field("verilog_name", verilog_name) .field("parameters", parameters) + .field("clocks_for_past", clocks_for_past) .field("simulation", simulation); } } @@ -1771,8 +1828,13 @@ impl AssertValidityState { ModuleBody::Extern(ExternModuleBody { verilog_name: _, parameters: _, + clocks_for_past, simulation: _, - }) => {} + }) => { + for clock_for_past in clocks_for_past { + validate_clock_for_past(Some(clock_for_past), &self.module.module_io); + } + } ModuleBody::Normal(NormalModuleBody { body }) => { let body = self.make_block_index(body); assert_eq!(body, 0); @@ -1802,9 +1864,17 @@ impl Module { match &mut body { ModuleBody::Normal(_) => {} ModuleBody::Extern(ExternModuleBody { + verilog_name: _, + parameters: _, + clocks_for_past, simulation: Some(simulation), - .. }) => { + let mut clocks_for_past_set = HashSet::default(); + *clocks_for_past = clocks_for_past + .iter() + .copied() + .filter(|clock_for_past| clocks_for_past_set.insert(*clock_for_past)) + .collect(); if module_io.iter().any(|io| { !simulation .sim_io_to_generator_map @@ -1965,7 +2035,7 @@ impl RegBuilder { init: _, ty: _, } = self; - let ty = Expr::ty(init); + let ty = init.ty(); RegBuilder { name, source_location, @@ -2111,6 +2181,27 @@ impl ModuleBuilder { self.output_with_loc(implicit_name.0, SourceLocation::caller(), ty) } #[track_caller] + pub fn add_platform_io_with_loc( + &self, + name: &str, + source_location: SourceLocation, + platform_io_builder: PlatformIOBuilder<'_>, + ) -> Expr { + platform_io_builder.add_platform_io(name, source_location, self) + } + #[track_caller] + pub fn add_platform_io( + &self, + implicit_name: ImplicitName<'_>, + platform_io_builder: PlatformIOBuilder<'_>, + ) -> Expr { + self.add_platform_io_with_loc( + implicit_name.0, + SourceLocation::caller(), + platform_io_builder, + ) + } + #[track_caller] pub fn run( name: &str, module_kind: ModuleKind, @@ -2156,6 +2247,7 @@ impl ModuleBuilder { ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody { verilog_name: name.0, parameters: vec![], + clocks_for_past: vec![], simulation: None, }), ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody { @@ -2278,6 +2370,20 @@ impl ModuleBuilder { value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()), }); } + /// registers a [`Clock`] so you can use it with the [`ExternModuleSimulationState::read_past()`] family of functions. + /// + /// [`ExternModuleSimulationState::read_past()`]: crate::sim::ExternModuleSimulationState::read_past() + #[track_caller] + pub fn register_clock_for_past(&self, clock_for_past: impl ToExpr) { + let clock_for_past = clock_for_past.to_expr().target().as_deref().copied(); + let mut impl_ = self.impl_.borrow_mut(); + let clock_for_past = validate_clock_for_past(clock_for_past, &impl_.io); + impl_ + .body + .builder_extern_body() + .clocks_for_past + .push(clock_for_past); + } #[track_caller] pub fn extern_module_simulation(&self, generator: G) { let mut impl_ = self.impl_.borrow_mut(); @@ -2498,7 +2604,7 @@ pub fn enum_match_variants_helper( ModuleBuilder::with(|m| { let mut impl_ = m.impl_.borrow_mut(); let body = impl_.body.builder_normal_body(); - let enum_ty = Expr::ty(base); + let enum_ty = base.ty(); let outer_block: BlockId = m.block_stack.top(); let blocks = Interned::from_iter(enum_ty.variants().iter().map(|_| body.new_block())); body.block(outer_block).stmts.push( @@ -2550,7 +2656,7 @@ pub fn connect_any_with_loc( rhs, source_location, }; - connect.assert_validity_with_original_types(Expr::ty(lhs_orig), Expr::ty(rhs_orig)); + connect.assert_validity_with_original_types(lhs_orig.ty(), rhs_orig.ty()); ModuleBuilder::with(|m| { m.impl_ .borrow_mut() @@ -2665,7 +2771,7 @@ pub fn memory_with_init_and_loc( source_location: SourceLocation, ) -> MemBuilder { let initial_value = initial_value.to_expr(); - let mut retval = memory_array_with_loc(name, Expr::ty(initial_value), source_location); + let mut retval = memory_array_with_loc(name, initial_value.ty(), source_location); retval.initial_value(initial_value); retval } @@ -2708,6 +2814,15 @@ pub struct ModuleIO { source_location: SourceLocation, } +impl ValueType for ModuleIO { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.ty + } +} + impl fmt::Debug for ModuleIO { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ModuleIO") @@ -2735,6 +2850,22 @@ impl ModuleIO { source_location, } } + pub fn from_canonical(canonical_module_io: ModuleIO) -> Self { + let ModuleIO { + containing_module_name, + bundle_field, + id, + ty, + source_location, + } = canonical_module_io; + Self { + containing_module_name, + bundle_field, + id, + ty: T::from_canonical(ty), + source_location, + } + } pub fn bundle_field(&self) -> BundleField { self.bundle_field } @@ -2793,9 +2924,6 @@ impl ModuleIO { unreachable!() } } - pub fn ty(&self) -> T { - self.ty - } } #[derive(PartialEq, Eq, Hash, Clone, Copy)] diff --git a/crates/fayalite/src/module/transform/deduce_resets.rs b/crates/fayalite/src/module/transform/deduce_resets.rs index 57197adb..61167fda 100644 --- a/crates/fayalite/src/module/transform/deduce_resets.rs +++ b/crates/fayalite/src/module/transform/deduce_resets.rs @@ -6,7 +6,7 @@ use crate::{ bundle::{BundleField, BundleType}, enum_::{EnumType, EnumVariant}, expr::{ - ExprEnum, + ExprEnum, ValueType, ops::{self, ArrayLiteral}, target::{ Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, @@ -521,7 +521,7 @@ impl State { Entry::Vacant(entry) => ( *entry.insert(Resets::with_new_nodes( &mut self.reset_graph, - Expr::ty(expr), + expr.ty(), source_location, )), true, @@ -1020,7 +1020,7 @@ fn cast_bit_op( } macro_rules! match_arg_ty { ($($Variant:ident),*) => { - *match Expr::ty(arg) { + *match arg.ty() { CanonicalType::Array(_) | CanonicalType::Enum(_) | CanonicalType::Bundle(_) @@ -1660,7 +1660,8 @@ impl RunPassDispatch for AnyReg { let clock_domain = Expr::::from_canonical( Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0, ); - match Expr::ty(clock_domain) + match clock_domain + .ty() .field_by_name("rst".intern()) .expect("ClockDomain has rst field") .ty @@ -1802,6 +1803,7 @@ impl_run_pass_clone!([] ExternModuleParameter); impl_run_pass_clone!([] SIntValue); impl_run_pass_clone!([] std::ops::Range); impl_run_pass_clone!([] UIntValue); +impl_run_pass_clone!([] crate::vendor::xilinx::XilinxAnnotation); impl_run_pass_copy!([] BlackBoxInlineAnnotation); impl_run_pass_copy!([] BlackBoxPathAnnotation); impl_run_pass_copy!([] bool); @@ -2072,6 +2074,7 @@ impl_run_pass_for_struct! { impl[] RunPass for ExternModuleBody { verilog_name: _, parameters: _, + clocks_for_past: _, simulation: _, } } @@ -2217,6 +2220,7 @@ impl_run_pass_for_enum! { BlackBoxPath(v), DocString(v), CustomFirrtl(v), + Xilinx(v), } } diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index ccdecf6a..8902921d 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -5,12 +5,12 @@ use crate::{ bundle::{Bundle, BundleField, BundleType}, enum_::{Enum, EnumType, EnumVariant}, expr::{ - CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, + CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, ValueType, ops::{self, EnumLiteral}, }, hdl, int::UInt, - intern::{Intern, Interned, Memoize}, + intern::{Intern, InternSlice, Interned, Memoize}, memory::{DynPortType, Mem, MemPort}, module::{ Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, @@ -22,6 +22,7 @@ use crate::{ wire::Wire, }; use core::fmt; +use serde::{Deserialize, Serialize}; #[derive(Debug)] pub enum SimplifyEnumsError { @@ -527,7 +528,7 @@ fn connect_port( rhs: Expr, source_location: SourceLocation, ) { - if Expr::ty(lhs) == Expr::ty(rhs) { + if lhs.ty() == rhs.ty() { stmts.push( StmtConnect { lhs, @@ -538,7 +539,7 @@ fn connect_port( ); return; } - match (Expr::ty(lhs), Expr::ty(rhs)) { + match (lhs.ty(), rhs.ty()) { (CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => { let lhs = Expr::::from_canonical(lhs); for field in lhs_type.fields() { @@ -585,8 +586,8 @@ fn connect_port( | (CanonicalType::PhantomConst(_), _) | (CanonicalType::DynSimOnly(_), _) => unreachable!( "trying to connect memory ports:\n{:?}\n{:?}", - Expr::ty(lhs), - Expr::ty(rhs), + lhs.ty(), + rhs.ty(), ), } } @@ -606,20 +607,23 @@ fn match_int_tag( }; }; let mut retval = StmtIf { - cond: int_tag_expr - .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)), + cond: int_tag_expr.cmp_eq( + int_tag_expr + .ty() + .from_int_wrapping(next_to_last_variant_index), + ), source_location, blocks: [next_to_last_block, last_block], }; for (variant_index, block) in blocks_iter.rev() { retval = StmtIf { - cond: int_tag_expr.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)), + cond: int_tag_expr.cmp_eq(int_tag_expr.ty().from_int_wrapping(variant_index)), source_location, blocks: [ block, Block { memories: Default::default(), - stmts: [Stmt::from(retval)][..].intern(), + stmts: [Stmt::from(retval)].intern_slice(), }, ], }; @@ -656,7 +660,7 @@ impl Folder for State { ExprEnum::VariantAccess(op) => { let folded_base_expr = Expr::canonical(op.base()).fold(self)?; Ok(*Expr::expr_enum(self.handle_variant_access( - Expr::ty(op.base()), + op.base().ty(), folded_base_expr, op.variant_index(), )?)) @@ -866,7 +870,7 @@ impl Folder for State { }) => { let folded_expr = Expr::canonical(expr).fold(self)?; let folded_blocks = blocks.fold(self)?; - self.handle_match(Expr::ty(expr), folded_expr, source_location, &folded_blocks) + self.handle_match(expr.ty(), folded_expr, source_location, &folded_blocks) } Stmt::Connect(StmtConnect { lhs, @@ -877,8 +881,8 @@ impl Folder for State { let folded_rhs = rhs.fold(self)?; let mut output_stmts = vec![]; self.handle_stmt_connect( - Expr::ty(lhs), - Expr::ty(rhs), + lhs.ty(), + rhs.ty(), folded_lhs, folded_rhs, source_location, @@ -955,12 +959,15 @@ impl Folder for State { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] pub enum SimplifyEnumsKind { SimplifyToEnumsWithNoBody, #[clap(name = "replace-with-bundle-of-uints")] + #[serde(rename = "replace-with-bundle-of-uints")] ReplaceWithBundleOfUInts, #[clap(name = "replace-with-uint")] + #[serde(rename = "replace-with-uint")] ReplaceWithUInt, } diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index 35f186d3..d7418361 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -4,7 +4,7 @@ use crate::{ annotations::TargetedAnnotation, array::Array, bundle::{Bundle, BundleType}, - expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr}, + expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr, ValueType}, int::{Bool, SInt, Size, UInt}, intern::{Intern, Interned}, memory::{Mem, MemPort, PortType}, @@ -530,7 +530,7 @@ impl ModuleState { connect_read( output_stmts, wire_read, - Expr::::from_canonical(port_read).cast_bits_to(Expr::ty(wire_read)), + Expr::::from_canonical(port_read).cast_bits_to(wire_read.ty()), ); }; let connect_write_enum = diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 44aabc38..2869a49a 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -11,7 +11,7 @@ use crate::{ clock::Clock, enum_::{Enum, EnumType, EnumVariant}, expr::{ - Expr, ExprEnum, ops, + Expr, ExprEnum, ValueType, ops, target::{ Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, @@ -33,6 +33,9 @@ use crate::{ sim::{ExternModuleSimulation, value::DynSimOnly}, source_location::SourceLocation, ty::{CanonicalType, Type}, + vendor::xilinx::{ + XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation, + }, wire::Wire, }; use num_bigint::{BigInt, BigUint}; diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index b8520560..32e9d6b9 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -2,13 +2,10 @@ // See Notices.txt for copyright information use crate::{ - expr::{ - Expr, ToExpr, - ops::{ExprPartialEq, ExprPartialOrd}, - }, + expr::{Expr, HdlPartialEqImpl, HdlPartialOrdImpl, ToExpr, ValueType}, int::Bool, intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize}, - sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, + sim::value::{SimValue, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, @@ -22,6 +19,7 @@ use serde::{ }; use std::{ any::Any, + borrow::Cow, fmt, hash::{Hash, Hasher}, marker::PhantomData, @@ -131,7 +129,7 @@ impl Index for PhantomConstWithoutGenerics { type Output = PhantomConst; fn index(&self, value: T) -> &Self::Output { - Interned::into_inner(PhantomConst::new(value.intern()).intern_sized()) + Interned::into_inner(PhantomConst::new(&value).intern_sized()) } } @@ -222,11 +220,26 @@ impl Memoize for PhantomConstCanonicalMemoize PhantomConst { - pub fn new(value: Interned) -> Self { + pub fn new_interned(value: Interned) -> Self { Self { value: LazyInterned::Interned(value), } } + pub fn new_sized(value: T) -> Self + where + T: Clone, + { + Self::new_interned(value.intern_sized()) + } + pub fn new(value: &T) -> Self { + Self::new_interned(value.intern()) + } + pub fn new_deref>(value: U) -> Self + where + T: ToOwned, + { + Self::new_interned(value.intern_deref()) + } pub const fn new_lazy(v: &'static dyn LazyInternedTrait) -> Self { Self { value: LazyInterned::new_lazy(v), @@ -245,7 +258,7 @@ impl PhantomConst { if let Some(&retval) = ::downcast_ref::(&self) { return retval; } - ::new( + ::new_interned( PhantomConstCanonicalMemoize::(PhantomData).get_owned(self.get()), ) } @@ -253,7 +266,7 @@ impl PhantomConst { if let Some(&retval) = ::downcast_ref::(&canonical_type) { return retval; } - Self::new( + Self::new_interned( PhantomConstCanonicalMemoize::(PhantomData).get_owned(canonical_type.get()), ) } @@ -346,7 +359,9 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst { D: Deserializer<'de>, { match SerdeType::::deserialize(deserializer)? { - SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => Ok(Self::new(value)), + SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => { + Ok(Self::new_interned(value)) + } ty => Err(Error::invalid_value( serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), &"a PhantomConst", @@ -355,50 +370,102 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for 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)); +impl HdlPartialEqImpl for PhantomConst { + #[track_caller] + fn cmp_value_eq( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); true } + + #[track_caller] + fn cmp_expr_eq(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + true.to_expr() + } + + #[track_caller] + fn cmp_expr_ne(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + false.to_expr() + } +} + +impl HdlPartialOrdImpl for PhantomConst { + #[track_caller] + fn cmp_value_lt( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); + false + } + + #[track_caller] + fn cmp_value_le( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); + true + } + + #[track_caller] + fn cmp_value_gt( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); + false + } + + #[track_caller] + fn cmp_value_ge( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); + true + } + + #[track_caller] + fn cmp_expr_lt(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + false.to_expr() + } + + #[track_caller] + fn cmp_expr_le(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + true.to_expr() + } + + #[track_caller] + fn cmp_expr_gt(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + false.to_expr() + } + + #[track_caller] + fn cmp_expr_ge(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + true.to_expr() + } } impl ToSimValue for PhantomConst { - type Type = PhantomConst; - fn to_sim_value(&self) -> SimValue { SimValue::from_value(*self, *self) } @@ -415,3 +482,71 @@ impl ToSimValueWithType for Phanto SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self)) } } + +mod sealed { + pub trait Sealed {} +} + +pub trait PhantomConstGet: sealed::Sealed { + fn get(&self) -> Interned; +} + +impl>> + sealed::Sealed for This +{ +} + +impl>> + PhantomConstGet for This +{ + fn get(&self) -> Interned { + This::Target::get(&**self) + } +} + +macro_rules! impl_phantom_const_get { + ( + impl PhantomConstGet<$T:ident> for $ty:ty { + fn $get:ident(&$get_self:ident) -> _ $get_body:block + } + ) => { + impl<$T: ?Sized + PhantomConstValue> sealed::Sealed<$T> for $ty {} + + impl<$T: ?Sized + PhantomConstValue> PhantomConstGet<$T> for $ty { + fn $get(&$get_self) -> Interned<$T> $get_body + } + }; +} + +impl_phantom_const_get! { + impl PhantomConstGet for PhantomConst { + fn get(&self) -> _ { + PhantomConst::get(*self) + } + } +} + +impl_phantom_const_get! { + impl PhantomConstGet for Expr> { + fn get(&self) -> _ { + PhantomConst::get(self.ty()) + } + } +} + +#[doc(hidden)] +pub trait ReturnSelfUnchanged { + type Type: ?Sized; +} + +impl ReturnSelfUnchanged for This { + type Type = This; +} + +#[doc(hidden)] +pub fn type_alias_phantom_const_get_helper( + param: impl PhantomConstGet, + get: impl FnOnce(Interned) -> R, +) -> &'static R { + Interned::into_inner(get(param.get()).intern_sized()) +} diff --git a/crates/fayalite/src/platform.rs b/crates/fayalite/src/platform.rs new file mode 100644 index 00000000..c6901cf1 --- /dev/null +++ b/crates/fayalite/src/platform.rs @@ -0,0 +1,1923 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + bundle::{Bundle, BundleField, BundleType}, + expr::{Expr, ExprEnum, ValueType}, + intern::{Intern, Interned}, + module::{Module, ModuleBuilder, ModuleIO, connect_with_loc, instance_with_loc, wire_with_loc}, + source_location::SourceLocation, + ty::{CanonicalType, Type}, + util::{HashMap, HashSet, InternedStrCompareAsStr}, +}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; +use std::{ + any::{Any, TypeId}, + borrow::Cow, + cmp::Ordering, + collections::{BTreeMap, BTreeSet}, + convert::Infallible, + fmt, + hash::{Hash, Hasher}, + iter::FusedIterator, + marker::PhantomData, + mem, + sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock, RwLockWriteGuard}, +}; + +pub mod peripherals; + +trait DynPlatformTrait: 'static + Send + Sync + fmt::Debug { + fn as_any(&self) -> &dyn Any; + fn eq_dyn(&self, other: &dyn DynPlatformTrait) -> bool; + fn hash_dyn(&self, state: &mut dyn Hasher); + fn name_dyn(&self) -> Interned; + fn new_peripherals_dyn<'builder>( + &self, + builder_factory: PeripheralsBuilderFactory<'builder>, + ) -> (DynPeripherals, PeripheralsBuilderFinished<'builder>); + fn source_location_dyn(&self) -> SourceLocation; + #[track_caller] + fn add_peripherals_in_wrapper_module_dyn(&self, m: &ModuleBuilder, peripherals: DynPeripherals); + fn aspects_dyn(&self) -> PlatformAspectSet; +} + +impl DynPlatformTrait for T { + fn as_any(&self) -> &dyn Any { + self + } + + fn eq_dyn(&self, other: &dyn DynPlatformTrait) -> bool { + other + .as_any() + .downcast_ref::() + .is_some_and(|other| self == other) + } + + fn hash_dyn(&self, mut state: &mut dyn Hasher) { + self.hash(&mut state); + } + + fn name_dyn(&self) -> Interned { + self.name() + } + + fn new_peripherals_dyn<'builder>( + &self, + builder_factory: PeripheralsBuilderFactory<'builder>, + ) -> (DynPeripherals, PeripheralsBuilderFinished<'builder>) { + let (peripherals, finished) = self.new_peripherals(builder_factory); + (DynPeripherals(Box::new(peripherals)), finished) + } + + fn source_location_dyn(&self) -> SourceLocation { + self.source_location() + } + + #[track_caller] + fn add_peripherals_in_wrapper_module_dyn( + &self, + m: &ModuleBuilder, + peripherals: DynPeripherals, + ) { + if DynPeripheralsTrait::type_id(&*peripherals.0) != TypeId::of::() { + panic!( + "wrong DynPeripherals value type, expected type: <{}>::Peripherals, got value:\n{peripherals:?}", + std::any::type_name::() + ); + } + let Ok(peripherals) = peripherals.0.into_box_any().downcast() else { + unreachable!(); + }; + self.add_peripherals_in_wrapper_module(m, *peripherals) + } + + fn aspects_dyn(&self) -> PlatformAspectSet { + self.aspects() + } +} + +#[derive(Clone)] +pub struct DynPlatform(Arc); + +impl PartialEq for DynPlatform { + fn eq(&self, other: &Self) -> bool { + DynPlatformTrait::eq_dyn(&*self.0, &*other.0) + } +} + +impl Eq for DynPlatform {} + +impl Hash for DynPlatform { + fn hash(&self, state: &mut H) { + DynPlatformTrait::hash_dyn(&*self.0, state); + } +} + +impl fmt::Debug for DynPlatform { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl DynPlatform { + pub fn new(platform: T) -> Self { + if let Some(platform) = ::downcast_ref::(&platform) { + platform.clone() + } else { + Self(Arc::new(platform)) + } + } +} + +trait DynPeripheralsTrait: fmt::Debug + 'static + Send + Sync { + fn type_id(&self) -> TypeId; + fn into_box_any(self: Box) -> Box; + fn append_peripherals_dyn<'a>( + &'a self, + peripherals: &mut Vec>, + ); +} + +impl DynPeripheralsTrait for T { + fn type_id(&self) -> TypeId { + TypeId::of::() + } + fn into_box_any(self: Box) -> Box { + self + } + fn append_peripherals_dyn<'a>( + &'a self, + peripherals: &mut Vec>, + ) { + self.append_peripherals(peripherals); + } +} + +pub struct DynPeripherals(Box); + +impl fmt::Debug for DynPeripherals { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Peripherals for DynPeripherals { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { + self.0.append_peripherals_dyn(peripherals); + } +} + +impl Platform for DynPlatform { + type Peripherals = DynPeripherals; + fn name(&self) -> Interned { + DynPlatformTrait::name_dyn(&*self.0) + } + fn new_peripherals<'a>( + &self, + builder_factory: PeripheralsBuilderFactory<'a>, + ) -> (Self::Peripherals, PeripheralsBuilderFinished<'a>) { + DynPlatformTrait::new_peripherals_dyn(&*self.0, builder_factory) + } + fn source_location(&self) -> SourceLocation { + DynPlatformTrait::source_location_dyn(&*self.0) + } + #[track_caller] + fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) { + DynPlatformTrait::add_peripherals_in_wrapper_module_dyn(&*self.0, m, peripherals); + } + fn aspects(&self) -> PlatformAspectSet { + DynPlatformTrait::aspects_dyn(&*self.0) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct PeripheralId { + pub name: Interned, +} + +impl PartialOrd for PeripheralId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PeripheralId { + fn cmp(&self, other: &Self) -> Ordering { + if self == other { + Ordering::Equal + } else { + let Self { name } = self; + str::cmp(name, &other.name) + } + } +} + +struct CollectingPeripherals { + conflicts_graph: BTreeMap>, + on_use_state: PeripheralsOnUseState, +} + +pub trait PeripheralsOnUseSharedState: 'static + Send + fmt::Debug { + fn as_any(&mut self) -> &mut dyn Any; +} + +impl PeripheralsOnUseSharedState for T { + fn as_any(&mut self) -> &mut dyn Any { + self + } +} + +type DynPeripheralsOnUse = dyn FnOnce( + &mut dyn PeripheralsOnUseSharedState, + PeripheralRef<'_, CanonicalType>, + Expr, + ) + Send + + 'static; + +struct PeripheralsOnUseState { + shared_state: Box, + main_module_io_fields: Vec, + main_module_io_wires: Vec>, + on_use_functions: BTreeMap>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum PeripheralAvailability { + Available, + Used, + ConflictsWithUsed(PeripheralId), +} + +impl PeripheralAvailability { + pub fn is_available(self) -> bool { + matches!(self, Self::Available) + } + pub fn is_used(&self) -> bool { + matches!(self, Self::Used) + } +} + +struct PeripheralsStateBuildingModule { + conflicts_graph: Interned>>>, + availabilities: Mutex>, + on_use_state: Mutex, +} + +impl From for PeripheralsStateBuildingModule { + fn from(value: CollectingPeripherals) -> Self { + let CollectingPeripherals { + conflicts_graph, + on_use_state, + } = value; + let conflicts_graph = BTreeMap::from_iter( + conflicts_graph + .into_iter() + .map(|(k, v)| (k, v.intern_sized())), + ) + .intern_sized(); + Self { + conflicts_graph, + availabilities: Mutex::new( + on_use_state + .on_use_functions + .keys() + .map(|&id| (id, PeripheralAvailability::Available)) + .collect(), + ), + on_use_state: Mutex::new(on_use_state), + } + } +} + +struct PeripheralsStateBuildingWrapperModule { + output_module_io: ModuleIO, + output: Option>, +} + +enum PeripheralsStateEnum { + Initial, + CollectingPeripherals(CollectingPeripherals), + BuildingModule, + BuildingWrapperModule(PeripheralsStateBuildingWrapperModule), +} + +struct PeripheralsState { + will_build_wrapper: bool, + state: Mutex, + building_module: OnceLock, +} + +impl PeripheralsState { + fn finish_collecting_peripherals(&self) { + let mut state = self.state.lock().expect("shouldn't be poison"); + let building_module = match mem::replace(&mut *state, PeripheralsStateEnum::BuildingModule) + { + PeripheralsStateEnum::CollectingPeripherals(v) => v.into(), + PeripheralsStateEnum::Initial + | PeripheralsStateEnum::BuildingModule + | PeripheralsStateEnum::BuildingWrapperModule(_) => unreachable!(), + }; + self.building_module.get_or_init(|| building_module); + } +} + +struct PeripheralCommon { + type_id: TypeId, + id: PeripheralId, + is_input: bool, + peripherals_state: Arc, +} + +#[must_use] +pub struct PeripheralsBuilderFactory<'a> { + peripherals_state: Arc, + _phantom: PhantomData &'a ()>, +} + +impl fmt::Debug for PeripheralsBuilderFactory<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PeripheralsBuilderFactory") + .finish_non_exhaustive() + } +} + +impl PeripheralsBuilderFactory<'_> { + fn new(will_build_wrapper: bool) -> Self { + Self { + peripherals_state: Arc::new(PeripheralsState { + will_build_wrapper, + state: Mutex::new(PeripheralsStateEnum::Initial), + building_module: OnceLock::new(), + }), + _phantom: PhantomData, + } + } +} + +impl<'a> PeripheralsBuilderFactory<'a> { + pub fn builder(self) -> PeripheralsBuilder<'a> { + self.builder_with_default_state() + } + pub fn builder_with_default_state( + self, + ) -> PeripheralsBuilder<'a, S> { + self.builder_with_boxed_state(Box::default()) + } + pub fn builder_with_state( + self, + shared_state: S, + ) -> PeripheralsBuilder<'a, S> { + self.builder_with_boxed_state(Box::new(shared_state)) + } + pub fn builder_with_boxed_state( + self, + shared_state: Box, + ) -> PeripheralsBuilder<'a, S> { + let Self { + peripherals_state, + _phantom: PhantomData, + } = self; + match *peripherals_state.state.lock().expect("shouldn't be poison") { + ref mut state @ PeripheralsStateEnum::Initial => { + *state = PeripheralsStateEnum::CollectingPeripherals(CollectingPeripherals { + conflicts_graph: BTreeMap::new(), + on_use_state: PeripheralsOnUseState { + shared_state, + main_module_io_fields: Vec::new(), + main_module_io_wires: Vec::new(), + on_use_functions: BTreeMap::new(), + }, + }) + } + PeripheralsStateEnum::CollectingPeripherals(_) + | PeripheralsStateEnum::BuildingModule + | PeripheralsStateEnum::BuildingWrapperModule(_) => unreachable!(), + } + PeripheralsBuilder { + peripherals_state, + _phantom: PhantomData, + } + } +} + +#[must_use] +pub struct PeripheralsBuilder<'a, S: PeripheralsOnUseSharedState = ()> { + peripherals_state: Arc, + _phantom: PhantomData<(Arc, fn(&'a ()) -> &'a ())>, +} + +#[must_use] +pub struct PeripheralsBuilderFinished<'a> { + _private: PhantomData &'a ()>, +} + +impl fmt::Debug for PeripheralsBuilderFinished<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PeripheralsBuilderFinished") + .finish_non_exhaustive() + } +} + +impl<'a, S: PeripheralsOnUseSharedState> PeripheralsBuilder<'a, S> { + fn state_enum(&mut self) -> MutexGuard<'_, PeripheralsStateEnum> { + self.peripherals_state + .state + .lock() + .expect("shouldn't be poison") + } + #[track_caller] + pub fn peripheral( + &mut self, + id_name: impl AsRef, + is_input: bool, + ty: T, + ) -> Peripheral { + self.peripheral_with_on_use(id_name, is_input, ty, |_, _, _| {}) + } + #[track_caller] + pub fn peripheral_with_on_use( + &mut self, + id_name: impl AsRef, + is_input: bool, + ty: T, + on_use: impl FnOnce(&mut S, PeripheralRef<'_, T>, Expr) + Send + 'static, + ) -> Peripheral { + let mut state_enum = self.state_enum(); + let PeripheralsStateEnum::CollectingPeripherals(CollectingPeripherals { + conflicts_graph, + on_use_state: + PeripheralsOnUseState { + shared_state: _, + main_module_io_fields: _, + main_module_io_wires: _, + on_use_functions, + }, + }) = &mut *state_enum + else { + unreachable!(); + }; + let id = PeripheralId { + name: id_name.as_ref().intern(), + }; + let std::collections::btree_map::Entry::Vacant(entry) = conflicts_graph.entry(id) else { + drop(state_enum); // don't poison + panic!("duplicate peripheral: {id:?}"); + }; + entry.insert(BTreeSet::new()); + on_use_functions.insert( + id, + Box::new(move |state, peripheral_ref, wire| { + on_use( + ::downcast_mut::(PeripheralsOnUseSharedState::as_any(state)) + .expect("known to be correct type"), + PeripheralRef::from_canonical(peripheral_ref), + Expr::from_canonical(wire), + ) + }), + ); + drop(state_enum); + Peripheral { + ty, + common: PeripheralCommon { + type_id: TypeId::of::(), + id, + is_input, + peripherals_state: self.peripherals_state.clone(), + }, + } + } + #[track_caller] + pub fn input_peripheral_with_on_use( + &mut self, + id_name: impl AsRef, + ty: T, + on_use: impl FnOnce(&mut S, PeripheralRef<'_, T>, Expr) + Send + 'static, + ) -> Peripheral { + self.peripheral_with_on_use(id_name, true, ty, on_use) + } + #[track_caller] + pub fn output_peripheral_with_on_use( + &mut self, + id_name: impl AsRef, + ty: T, + on_use: impl FnOnce(&mut S, PeripheralRef<'_, T>, Expr) + Send + 'static, + ) -> Peripheral { + self.peripheral_with_on_use(id_name, false, ty, on_use) + } + #[track_caller] + pub fn input_peripheral(&mut self, id_name: impl AsRef, ty: T) -> Peripheral { + self.peripheral(id_name, true, ty) + } + #[track_caller] + pub fn output_peripheral(&mut self, id_name: impl AsRef, ty: T) -> Peripheral { + self.peripheral(id_name, false, ty) + } + #[track_caller] + pub fn add_conflicts(&mut self, conflicts: impl AsRef<[PeripheralId]>) { + let mut state_enum = self.state_enum(); + let PeripheralsStateEnum::CollectingPeripherals(collecting_peripherals) = &mut *state_enum + else { + unreachable!(); + }; + let conflicts = conflicts.as_ref(); + for &id in conflicts { + let Some(conflicts_for_id) = collecting_peripherals.conflicts_graph.get_mut(&id) else { + drop(state_enum); // don't poison + panic!("unknown peripheral: {id:?}"); + }; + conflicts_for_id.extend(conflicts.iter().copied().filter(|&v| v != id)); + } + } + pub fn finish(self) -> PeripheralsBuilderFinished<'a> { + self.peripherals_state.finish_collecting_peripherals(); + PeripheralsBuilderFinished { + _private: PhantomData, + } + } +} + +#[must_use] +pub struct Peripheral { + ty: T, + common: PeripheralCommon, +} + +impl Peripheral { + pub fn as_ref<'a>(&'a self) -> PeripheralRef<'a, T> { + let Self { ty, ref common } = *self; + PeripheralRef { ty, common } + } + pub fn ty(&self) -> T { + self.as_ref().ty() + } + pub fn id(&self) -> PeripheralId { + self.as_ref().id() + } + pub fn name(&self) -> Interned { + self.as_ref().name() + } + pub fn is_input(&self) -> bool { + self.as_ref().is_input() + } + pub fn is_output(&self) -> bool { + self.as_ref().is_output() + } + pub fn conflicts_with(&self) -> Interned> { + self.as_ref().conflicts_with() + } + pub fn availability(&self) -> PeripheralAvailability { + self.as_ref().availability() + } + pub fn is_available(&self) -> bool { + self.as_ref().is_available() + } + pub fn is_used(&self) -> bool { + self.as_ref().is_used() + } + pub fn try_into_used(self) -> Result, Self> { + let Some(building_module) = self.common.peripherals_state.building_module.get() else { + return Err(self); + }; + let building_module = building_module + .availabilities + .lock() + .expect("shouldn't be poison"); + match building_module[&self.common.id] { + PeripheralAvailability::Used => {} + PeripheralAvailability::Available | PeripheralAvailability::ConflictsWithUsed(_) => { + drop(building_module); + return Err(self); + } + } + drop(building_module); + let state = self + .common + .peripherals_state + .state + .lock() + .expect("shouldn't be poison"); + let output = match *state { + PeripheralsStateEnum::Initial | PeripheralsStateEnum::CollectingPeripherals(_) => { + unreachable!() + } + PeripheralsStateEnum::BuildingModule => { + drop(state); + return Err(self); + } + PeripheralsStateEnum::BuildingWrapperModule( + PeripheralsStateBuildingWrapperModule { + output: Some(output), + .. + }, + ) => output, + PeripheralsStateEnum::BuildingWrapperModule(_) => unreachable!(), + }; + drop(state); + let Self { ty, common } = self; + let instance_io_field = Expr::field::(output, &common.id.name); + assert_eq!(ty, instance_io_field.ty()); + Ok(UsedPeripheral { + instance_io_field, + common, + }) + } + pub fn into_used(self) -> Option> { + self.try_into_used().ok() + } +} + +impl fmt::Debug for Peripheral { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_ref().debug_common_fields("Peripheral", f).finish() + } +} + +pub struct UsedPeripheral { + instance_io_field: Expr, + common: PeripheralCommon, +} + +impl fmt::Debug for UsedPeripheral { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_ref() + .debug_common_fields("UsedPeripheral", f) + .field("instance_io_field", &self.instance_io_field()) + .finish() + } +} + +impl UsedPeripheral { + pub fn as_ref<'a>(&'a self) -> PeripheralRef<'a, T> { + let Self { + instance_io_field, + ref common, + } = *self; + PeripheralRef { + ty: instance_io_field.ty(), + common, + } + } + pub fn instance_io_field(&self) -> Expr { + self.instance_io_field + } + pub fn ty(&self) -> T { + self.as_ref().ty() + } + pub fn id(&self) -> PeripheralId { + self.as_ref().id() + } + pub fn name(&self) -> Interned { + self.as_ref().name() + } + pub fn is_input(&self) -> bool { + self.as_ref().is_input() + } + pub fn is_output(&self) -> bool { + self.as_ref().is_output() + } + pub fn conflicts_with(&self) -> Interned> { + self.as_ref().conflicts_with() + } +} + +#[derive(Copy, Clone)] +pub struct PeripheralRef<'a, T: Type> { + ty: T, + common: &'a PeripheralCommon, +} + +impl<'a, T: Type> fmt::Debug for PeripheralRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_common_fields("PeripheralRef", f).finish() + } +} + +#[derive(Debug, Clone)] +pub enum PeripheralUnavailableError { + PeripheralAlreadyUsed { + id: PeripheralId, + }, + PeripheralConflict { + id: PeripheralId, + conflicting_id: PeripheralId, + }, + PeripheralsWillNotBeUsedToBuildWrapper, +} + +impl fmt::Display for PeripheralUnavailableError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::PeripheralAlreadyUsed { id } => { + write!(f, "peripherals can only be used once: {id:?}") + } + Self::PeripheralConflict { id, conflicting_id } => { + write!(f, "peripheral {id:?} conflicts with {conflicting_id:?}") + } + Self::PeripheralsWillNotBeUsedToBuildWrapper => { + write!(f, "peripherals will not be used to build wrapper") + } + } + } +} + +impl std::error::Error for PeripheralUnavailableError {} + +impl<'a, T: Type> PeripheralRef<'a, T> { + fn debug_common_fields<'f1, 'f2>( + &self, + struct_name: &str, + f: &'f1 mut fmt::Formatter<'f2>, + ) -> fmt::DebugStruct<'f1, 'f2> { + let Self { + ty, + common: + PeripheralCommon { + type_id: _, + id, + is_input, + peripherals_state: _, + }, + } = self; + let mut retval = f.debug_struct(struct_name); + retval + .field("ty", ty) + .field("id", id) + .field("is_input", is_input) + .field("availability", &self.availability()); + retval + } + pub fn ty(&self) -> T { + self.ty + } + pub fn id(&self) -> PeripheralId { + self.common.id + } + pub fn name(&self) -> Interned { + self.id().name + } + pub fn is_input(&self) -> bool { + self.common.is_input + } + pub fn is_output(&self) -> bool { + !self.common.is_input + } + pub fn conflicts_with(&self) -> Interned> { + match self.common.peripherals_state.building_module.get() { + Some(building_module) => building_module.conflicts_graph[&self.common.id], + None => match &*self + .common + .peripherals_state + .state + .lock() + .expect("shouldn't be poison") + { + PeripheralsStateEnum::CollectingPeripherals(v) => { + v.conflicts_graph[&self.common.id].intern() + } + PeripheralsStateEnum::Initial + | PeripheralsStateEnum::BuildingModule + | PeripheralsStateEnum::BuildingWrapperModule(_) => unreachable!(), + }, + } + } + pub fn availability(&self) -> PeripheralAvailability { + match self.common.peripherals_state.building_module.get() { + None => PeripheralAvailability::Available, + Some(building_module) => building_module + .availabilities + .lock() + .expect("shouldn't be poison")[&self.common.id], + } + } + pub fn is_available(&self) -> bool { + match self.common.peripherals_state.building_module.get() { + None => true, + Some(building_module) => match building_module + .availabilities + .lock() + .expect("shouldn't be poison")[&self.common.id] + { + PeripheralAvailability::Available => true, + PeripheralAvailability::Used | PeripheralAvailability::ConflictsWithUsed(_) => { + false + } + }, + } + } + pub fn is_used(&self) -> bool { + match self.common.peripherals_state.building_module.get() { + None => false, + Some(building_module) => match building_module + .availabilities + .lock() + .expect("shouldn't be poison")[&self.common.id] + { + PeripheralAvailability::Used => true, + PeripheralAvailability::Available + | PeripheralAvailability::ConflictsWithUsed(_) => false, + }, + } + } + pub fn canonical(self) -> PeripheralRef<'a, CanonicalType> { + let Self { ty, common } = self; + PeripheralRef { + ty: ty.canonical(), + common, + } + } + pub fn from_canonical(peripheral_ref: PeripheralRef<'a, CanonicalType>) -> Self { + let PeripheralRef { ty, common } = peripheral_ref; + Self { + ty: T::from_canonical(ty), + common, + } + } + #[track_caller] + pub fn try_use_peripheral(self) -> Result, PeripheralUnavailableError> { + self.try_use_peripheral_with_loc(SourceLocation::caller()) + } + #[track_caller] + pub fn try_use_peripheral_with_loc( + self, + source_location: SourceLocation, + ) -> Result, PeripheralUnavailableError> { + let PeripheralsState { + will_build_wrapper, + ref state, + ref building_module, + } = *self.common.peripherals_state; + if !will_build_wrapper { + return Err(PeripheralUnavailableError::PeripheralsWillNotBeUsedToBuildWrapper); + } + let Some(PeripheralsStateBuildingModule { + conflicts_graph, + availabilities, + on_use_state, + }) = building_module.get() + else { + panic!("can't use peripherals in a module before the PeripheralsBuilder is finished"); + }; + let state = state.lock().expect("shouldn't be poison"); + match *state { + PeripheralsStateEnum::Initial | PeripheralsStateEnum::CollectingPeripherals(_) => { + unreachable!() + } + PeripheralsStateEnum::BuildingModule => {} + PeripheralsStateEnum::BuildingWrapperModule(_) => { + panic!("can't add new peripherals after calling m.add_platform_io()") + } + } + drop(state); + let mut availabilities = availabilities.lock().expect("shouldn't be poison"); + let Some(availability) = availabilities.get_mut(&self.common.id) else { + unreachable!(); + }; + match *availability { + PeripheralAvailability::Available => { + *availability = PeripheralAvailability::Used; + } + PeripheralAvailability::Used => { + return Err(PeripheralUnavailableError::PeripheralAlreadyUsed { + id: self.common.id, + }); + } + PeripheralAvailability::ConflictsWithUsed(conflicting_id) => { + return Err(PeripheralUnavailableError::PeripheralConflict { + id: self.common.id, + conflicting_id, + }); + } + } + for conflict in conflicts_graph[&self.common.id].iter() { + let Some(availability) = availabilities.get_mut(conflict) else { + unreachable!(); + }; + *availability = PeripheralAvailability::ConflictsWithUsed(self.common.id); + } + drop(availabilities); + let wire = wire_with_loc(&self.name(), source_location, self.ty()); + let mut on_use_state = on_use_state.lock().expect("shouldn't be poison"); + let PeripheralsOnUseState { + shared_state, + main_module_io_fields, + main_module_io_wires, + on_use_functions, + } = &mut *on_use_state; + let Some(on_use_function) = on_use_functions.remove(&self.common.id) else { + unreachable!(); + }; + for conflict in conflicts_graph[&self.common.id].iter() { + on_use_functions.remove(conflict); + } + let canonical_wire = Expr::canonical(wire); + main_module_io_wires.push(canonical_wire); + main_module_io_fields.push(BundleField { + name: self.name(), + flipped: self.is_input(), + ty: canonical_wire.ty(), + }); + on_use_function(&mut **shared_state, self.canonical(), canonical_wire); + drop(on_use_state); + Ok(wire) + } + #[track_caller] + pub fn use_peripheral_with_loc(self, source_location: SourceLocation) -> Expr { + match self.try_use_peripheral_with_loc(source_location) { + Ok(wire) => wire, + Err(e) => panic!("{e}"), + } + } + #[track_caller] + pub fn use_peripheral(self) -> Expr { + match self.try_use_peripheral() { + Ok(wire) => wire, + Err(e) => panic!("{e}"), + } + } +} + +pub trait Peripherals: 'static + Send + Sync + fmt::Debug { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>); + fn to_peripherals_vec<'a>(&'a self) -> Vec> { + let mut peripherals = Vec::new(); + self.append_peripherals(&mut peripherals); + peripherals + } +} + +impl Peripherals for Peripheral { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { + peripherals.push(self.as_ref().canonical()); + } +} + +impl Peripherals for Vec { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { + for v in self { + v.append_peripherals(peripherals); + } + } +} + +impl Peripherals for Box { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { + T::append_peripherals(self, peripherals); + } +} + +impl Peripherals for [T] { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { + for v in self { + v.append_peripherals(peripherals); + } + } +} + +impl Peripherals for [T; N] { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { + for v in self { + v.append_peripherals(peripherals); + } + } +} + +macro_rules! impl_peripherals { + (@impl $(($v:ident: $T:ident),)*) => { + impl<$($T: Peripherals),*> Peripherals for ($($T,)*) { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { + #![allow(unused_variables)] + let ($($v,)*) = self; + $(Peripherals::append_peripherals($v, peripherals);)* + } + } + }; + ($($first:tt, $($rest:tt,)*)?) => { + impl_peripherals!(@impl $($first, $($rest,)*)?); + $(impl_peripherals!($($rest,)*);)? + }; +} + +impl_peripherals! { + (v0: T0), + (v1: T1), + (v2: T2), + (v3: T3), + (v4: T4), + (v5: T5), + (v6: T6), + (v7: T7), + (v8: T8), + (v9: T9), + (v10: T10), + (v11: T11), +} + +pub struct PlatformIOBuilder<'a> { + peripherals: Vec>, + peripherals_by_type_id: HashMap>>, + peripherals_by_id: BTreeMap>, + peripherals_state: &'a PeripheralsState, +} + +impl<'a> PlatformIOBuilder<'a> { + pub fn peripherals(&self) -> &[PeripheralRef<'a, CanonicalType>] { + &self.peripherals + } + pub fn peripherals_with_type(&self) -> Vec> { + let Some(peripherals) = self.peripherals_by_type_id.get(&TypeId::of::()) else { + return Vec::new(); + }; + peripherals + .iter() + .map(|&peripheral_ref| PeripheralRef::from_canonical(peripheral_ref)) + .collect() + } + pub fn peripherals_by_id(&self) -> &BTreeMap> { + &self.peripherals_by_id + } + #[track_caller] + pub(crate) fn add_platform_io( + self, + name: &str, + source_location: SourceLocation, + m: &ModuleBuilder, + ) -> Expr { + if !ModuleBuilder::with(|m2| std::ptr::eq(m, m2)) { + panic!("m.add_platform_io() must be called in the same module as m"); + } + let PeripheralsState { + will_build_wrapper: _, + state, + building_module, + } = self.peripherals_state; + let building_module = building_module.get().expect("shouldn't be None"); + let mut on_use_state = building_module + .on_use_state + .lock() + .expect("shouldn't be poison"); + let output_ty = + Bundle::new(mem::take(&mut on_use_state.main_module_io_fields).intern_deref()); + let main_module_wires = mem::take(&mut on_use_state.main_module_io_wires); + drop(on_use_state); + let output = m.output_with_loc(name, source_location, output_ty); + for (field, wire_expr) in output_ty.fields().iter().zip(main_module_wires) { + let ExprEnum::Wire(wire) = *Expr::expr_enum(wire_expr) else { + unreachable!(); + }; + let field_expr = Expr::field(output, &field.name); + if field.flipped { + connect_with_loc(wire, field_expr, wire.source_location()); + } else { + connect_with_loc(field_expr, wire, wire.source_location()); + } + } + let ExprEnum::ModuleIO(output_module_io) = *Expr::expr_enum(output) else { + unreachable!(); + }; + *state.lock().expect("shouldn't be poison") = + PeripheralsStateEnum::BuildingWrapperModule(PeripheralsStateBuildingWrapperModule { + output_module_io, + output: None, + }); + output + } +} + +impl<'a> fmt::Debug for PlatformIOBuilder<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + peripherals, + peripherals_by_type_id: _, + peripherals_by_id: _, + peripherals_state: _, + } = self; + f.debug_struct("PlatformIOBuilder") + .field("peripherals", peripherals) + .finish_non_exhaustive() + } +} + +trait PlatformAspectTrait: 'static + Send + Sync + fmt::Debug { + fn any_ref(&self) -> &dyn Any; + fn any_arc(self: Arc) -> Arc; + fn eq_dyn(&self, other: &dyn PlatformAspectTrait) -> bool; + fn hash_dyn(&self, state: &mut dyn Hasher); +} + +impl PlatformAspectTrait for T { + fn any_ref(&self) -> &dyn Any { + self + } + + fn any_arc(self: Arc) -> Arc { + self + } + + fn eq_dyn(&self, other: &dyn PlatformAspectTrait) -> bool { + other + .any_ref() + .downcast_ref::() + .is_some_and(|other| self == other) + } + + fn hash_dyn(&self, mut state: &mut dyn Hasher) { + self.hash(&mut state); + } +} + +#[derive(Clone)] +pub struct PlatformAspect { + type_id: TypeId, + type_name: &'static str, + value: Arc, +} + +impl Hash for PlatformAspect { + fn hash(&self, state: &mut H) { + PlatformAspectTrait::hash_dyn(&*self.value, state); + } +} + +impl Eq for PlatformAspect {} + +impl PartialEq for PlatformAspect { + fn eq(&self, other: &Self) -> bool { + self.type_id == other.type_id && PlatformAspectTrait::eq_dyn(&*self.value, &*other.value) + } +} + +impl fmt::Debug for PlatformAspect { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + type_id: _, + type_name, + value, + } = self; + write!(f, "PlatformAspect<{type_name}>")?; + f.debug_tuple("").field(value).finish() + } +} + +impl PlatformAspect { + pub fn new_arc(value: Arc) -> Self { + Self { + type_id: TypeId::of::(), + type_name: std::any::type_name::(), + value, + } + } + pub fn new(value: T) -> Self { + Self::new_arc(Arc::new(value)) + } + pub fn type_id(&self) -> TypeId { + self.type_id + } + pub fn downcast_arc(self) -> Result, Self> { + if self.type_id == TypeId::of::() { + let Ok(retval) = self.value.any_arc().downcast() else { + unreachable!(); + }; + Ok(retval) + } else { + Err(self) + } + } + pub fn downcast_unwrap_or_clone( + self, + ) -> Result { + Ok(Arc::unwrap_or_clone(self.downcast_arc()?)) + } + pub fn downcast_ref(&self) -> Option<&T> { + PlatformAspectTrait::any_ref(&*self.value).downcast_ref() + } +} + +#[derive(Clone, Default)] +pub struct PlatformAspectSet { + aspects_by_type_id: Arc>>, + aspects: Arc>, +} + +impl PlatformAspectSet { + pub fn new() -> Self { + Self::default() + } + pub fn insert_new( + &mut self, + value: T, + ) -> bool { + self.insert(PlatformAspect::new(value)) + } + pub fn insert_new_arc( + &mut self, + value: Arc, + ) -> bool { + self.insert(PlatformAspect::new_arc(value)) + } + fn insert_inner( + aspects_by_type_id: &mut HashMap>, + aspects: &mut Vec, + value: PlatformAspect, + ) -> bool { + if aspects_by_type_id + .entry(value.type_id) + .or_default() + .insert(value.clone()) + { + aspects.push(value); + true + } else { + false + } + } + pub fn insert(&mut self, value: PlatformAspect) -> bool { + Self::insert_inner( + Arc::make_mut(&mut self.aspects_by_type_id), + Arc::make_mut(&mut self.aspects), + value, + ) + } + pub fn contains(&self, value: &PlatformAspect) -> bool { + self.aspects_by_type_id + .get(&value.type_id) + .is_some_and(|aspects| aspects.contains(value)) + } + pub fn get_aspects_by_type<'a, T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( + &'a self, + ) -> impl Clone + Iterator + FusedIterator + ExactSizeIterator + 'a + { + self.aspects_by_type_id + .get(&TypeId::of::()) + .map(|aspects| aspects.iter()) + .unwrap_or_default() + } + pub fn get_by_type<'a, T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( + &'a self, + ) -> impl Clone + Iterator + FusedIterator + ExactSizeIterator + 'a { + self.get_aspects_by_type::() + .map(|aspect| aspect.downcast_ref().expect("already checked type")) + } + pub fn get_single_by_type<'a, T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( + &'a self, + ) -> Option<&'a T> { + let mut aspects = self.get_by_type::(); + if aspects.len() == 1 { + aspects.next() + } else { + None + } + } + pub fn get_arcs_by_type<'a, T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( + &'a self, + ) -> impl Clone + Iterator> + FusedIterator + ExactSizeIterator + 'a { + self.get_aspects_by_type::().map(|aspect| { + aspect + .clone() + .downcast_arc() + .ok() + .expect("already checked type") + }) + } +} + +impl<'a> Extend<&'a PlatformAspect> for PlatformAspectSet { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().cloned()); + } +} + +impl Extend for PlatformAspectSet { + fn extend>(&mut self, iter: T) { + let Self { + aspects_by_type_id, + aspects, + } = self; + let aspects_by_type_id = Arc::make_mut(aspects_by_type_id); + let aspects = Arc::make_mut(aspects); + iter.into_iter().for_each(|value| { + Self::insert_inner(aspects_by_type_id, aspects, value); + }); + } +} + +impl<'a> FromIterator<&'a PlatformAspect> for PlatformAspectSet { + fn from_iter>(iter: T) -> Self { + let mut retval = Self::default(); + retval.extend(iter); + retval + } +} + +impl FromIterator for PlatformAspectSet { + fn from_iter>(iter: T) -> Self { + let mut retval = Self::default(); + retval.extend(iter); + retval + } +} + +impl std::ops::Deref for PlatformAspectSet { + type Target = [PlatformAspect]; + + fn deref(&self) -> &Self::Target { + &self.aspects + } +} + +impl fmt::Debug for PlatformAspectSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self).finish() + } +} + +impl IntoIterator for PlatformAspectSet { + type Item = PlatformAspect; + type IntoIter = PlatformAspectsIntoIter; + + fn into_iter(self) -> Self::IntoIter { + PlatformAspectsIntoIter { + indexes: 0..self.aspects.len(), + aspects: self.aspects, + } + } +} + +impl<'a> IntoIterator for &'a PlatformAspectSet { + type Item = &'a PlatformAspect; + type IntoIter = std::slice::Iter<'a, PlatformAspect>; + + fn into_iter(self) -> Self::IntoIter { + self.aspects.iter() + } +} + +#[derive(Clone, Debug)] +pub struct PlatformAspectsIntoIter { + aspects: Arc>, + indexes: std::ops::Range, +} + +impl Iterator for PlatformAspectsIntoIter { + type Item = PlatformAspect; + + fn next(&mut self) -> Option { + self.indexes.next().map(|index| self.aspects[index].clone()) + } + + fn size_hint(&self) -> (usize, Option) { + self.indexes.size_hint() + } + + fn count(self) -> usize { + self.indexes.len() + } + + fn last(mut self) -> Option { + self.next_back() + } + + fn nth(&mut self, n: usize) -> Option { + self.indexes.nth(n).map(|index| self.aspects[index].clone()) + } + + fn fold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.indexes + .fold(init, |v, index| f(v, self.aspects[index].clone())) + } +} + +impl FusedIterator for PlatformAspectsIntoIter {} + +impl ExactSizeIterator for PlatformAspectsIntoIter {} + +impl DoubleEndedIterator for PlatformAspectsIntoIter { + fn next_back(&mut self) -> Option { + self.indexes + .next_back() + .map(|index| self.aspects[index].clone()) + } + + fn nth_back(&mut self, n: usize) -> Option { + self.indexes + .nth_back(n) + .map(|index| self.aspects[index].clone()) + } + + fn rfold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.indexes + .rfold(init, |v, index| f(v, self.aspects[index].clone())) + } +} + +pub trait Platform: Clone + 'static + Send + Sync + fmt::Debug + Hash + Eq { + type Peripherals: Peripherals; + fn name(&self) -> Interned; + fn new_peripherals<'builder>( + &self, + builder_factory: PeripheralsBuilderFactory<'builder>, + ) -> (Self::Peripherals, PeripheralsBuilderFinished<'builder>); + /// gets peripherals that can be used for inspecting them, but not for building a main module + fn get_peripherals(&self) -> Self::Peripherals { + let ( + retval, + PeripheralsBuilderFinished { + _private: PhantomData, + }, + ) = self.new_peripherals(PeripheralsBuilderFactory::new(false)); + retval + } + fn source_location(&self) -> SourceLocation; + fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals); + #[track_caller] + fn try_wrap_main_module< + T: BundleType, + E, + M: AsRef>, + F: for<'a> FnOnce(PlatformIOBuilder<'a>) -> Result, + >( + &self, + make_main_module: F, + ) -> Result>, E> { + let builder_factory = PeripheralsBuilderFactory::new(true); + let peripherals_state = builder_factory.peripherals_state.clone(); + let ( + peripherals, + PeripheralsBuilderFinished { + _private: PhantomData, + }, + ) = self.new_peripherals(builder_factory); + let peripherals_vec = peripherals.to_peripherals_vec(); + let mut peripherals_by_id = BTreeMap::new(); + let mut peripherals_by_type_id = HashMap::<_, Vec<_>>::default(); + for &peripheral in &peripherals_vec { + peripherals_by_id.insert(peripheral.id(), peripheral); + peripherals_by_type_id + .entry(peripheral.common.type_id) + .or_default() + .push(peripheral); + } + let main_module = Module::canonical( + *make_main_module(PlatformIOBuilder { + peripherals: peripherals_vec, + peripherals_by_type_id, + peripherals_by_id, + peripherals_state: &peripherals_state, + })? + .as_ref(), + ); + let state = peripherals_state.state.lock().expect("shouldn't be poison"); + let PeripheralsStateEnum::BuildingWrapperModule(PeripheralsStateBuildingWrapperModule { + output_module_io, + output: _, + }) = *state + else { + drop(state); + panic!( + "you need to call m.add_platform_io() inside the main module you're trying to use peripherals in.\nat: {}", + main_module.source_location() + ); + }; + drop(state); + for module_io in main_module.module_io() { + if module_io.module_io != output_module_io { + panic!( + "when you're using m.add_platform_io(), you can't have any other inputs/outputs.\nat: {}", + module_io.module_io.source_location() + ); + } + } + Ok(ModuleBuilder::run_with_loc( + &main_module.name(), + self.source_location(), + crate::module::ModuleKind::Normal, + |m| { + let instance = + instance_with_loc("main", main_module.intern(), self.source_location()); + let output_expr = Expr::field(instance, &output_module_io.bundle_field().name); + let mut state = peripherals_state.state.lock().expect("shouldn't be poison"); + let PeripheralsStateEnum::BuildingWrapperModule( + PeripheralsStateBuildingWrapperModule { + output_module_io: _, + output, + }, + ) = &mut *state + else { + unreachable!(); + }; + *output = Some(output_expr); + drop(state); + self.add_peripherals_in_wrapper_module(m, peripherals) + }, + )) + } + #[track_caller] + fn wrap_main_module< + T: BundleType, + M: AsRef>, + F: for<'a> FnOnce(PlatformIOBuilder<'a>) -> M, + >( + &self, + make_main_module: F, + ) -> Interned> { + self.try_wrap_main_module(|p| Ok(make_main_module(p))) + .unwrap_or_else(|e: Infallible| match e {}) + } + fn aspects(&self) -> PlatformAspectSet; +} + +impl DynPlatform { + pub fn registry() -> PlatformRegistrySnapshot { + PlatformRegistrySnapshot(PlatformRegistry::get()) + } + #[track_caller] + pub fn register(self) { + PlatformRegistry::register(PlatformRegistry::lock(), self); + } +} + +#[derive(Clone, Debug)] +struct PlatformRegistry { + platforms: BTreeMap, +} + +enum PlatformRegisterError { + SameName { + name: InternedStrCompareAsStr, + old_platform: DynPlatform, + new_platform: DynPlatform, + }, +} + +impl fmt::Display for PlatformRegisterError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SameName { + name, + old_platform, + new_platform, + } => write!( + f, + "two different `Platform` can't share the same name:\n\ + {name:?}\n\ + old platform:\n\ + {old_platform:?}\n\ + new platform:\n\ + {new_platform:?}", + ), + } + } +} + +trait PlatformRegistryRegisterLock { + type Locked; + fn lock(self) -> Self::Locked; + fn make_mut(locked: &mut Self::Locked) -> &mut PlatformRegistry; +} + +impl PlatformRegistryRegisterLock for &'static RwLock> { + type Locked = RwLockWriteGuard<'static, Arc>; + fn lock(self) -> Self::Locked { + self.write().expect("shouldn't be poisoned") + } + fn make_mut(locked: &mut Self::Locked) -> &mut PlatformRegistry { + Arc::make_mut(locked) + } +} + +impl PlatformRegistryRegisterLock for &'_ mut PlatformRegistry { + type Locked = Self; + + fn lock(self) -> Self::Locked { + self + } + + fn make_mut(locked: &mut Self::Locked) -> &mut PlatformRegistry { + locked + } +} + +impl PlatformRegistry { + fn lock() -> &'static RwLock> { + static REGISTRY: OnceLock>> = OnceLock::new(); + REGISTRY.get_or_init(Default::default) + } + fn try_register( + lock: L, + platform: DynPlatform, + ) -> Result<(), PlatformRegisterError> { + use std::collections::btree_map::Entry; + let name = InternedStrCompareAsStr(platform.name()); + // run user code only outside of lock + let mut locked = lock.lock(); + let this = L::make_mut(&mut locked); + let result = match this.platforms.entry(name) { + Entry::Occupied(entry) => Err(PlatformRegisterError::SameName { + name, + old_platform: entry.get().clone(), + new_platform: platform, + }), + Entry::Vacant(entry) => { + entry.insert(platform); + Ok(()) + } + }; + drop(locked); + // outside of lock now, so we can test if it's the same DynPlatform + match result { + Err(PlatformRegisterError::SameName { + name: _, + old_platform, + new_platform, + }) if old_platform == new_platform => Ok(()), + result => result, + } + } + #[track_caller] + fn register(lock: L, platform: DynPlatform) { + match Self::try_register(lock, platform) { + Err(e) => panic!("{e}"), + Ok(()) => {} + } + } + fn get() -> Arc { + Self::lock().read().expect("shouldn't be poisoned").clone() + } +} + +impl Default for PlatformRegistry { + fn default() -> Self { + let mut retval = Self { + platforms: BTreeMap::new(), + }; + for platform in built_in_platforms() { + Self::register(&mut retval, platform); + } + retval + } +} + +#[derive(Clone, Debug)] +pub struct PlatformRegistrySnapshot(Arc); + +impl PlatformRegistrySnapshot { + pub fn get() -> Self { + PlatformRegistrySnapshot(PlatformRegistry::get()) + } + pub fn get_by_name<'a>(&'a self, name: &str) -> Option<&'a DynPlatform> { + self.0.platforms.get(name) + } + pub fn iter_with_names(&self) -> PlatformRegistryIterWithNames<'_> { + PlatformRegistryIterWithNames(self.0.platforms.iter()) + } + pub fn iter(&self) -> PlatformRegistryIter<'_> { + PlatformRegistryIter(self.0.platforms.values()) + } +} + +impl<'a> IntoIterator for &'a PlatformRegistrySnapshot { + type Item = &'a DynPlatform; + type IntoIter = PlatformRegistryIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a> IntoIterator for &'a mut PlatformRegistrySnapshot { + type Item = &'a DynPlatform; + type IntoIter = PlatformRegistryIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[derive(Clone, Debug)] +pub struct PlatformRegistryIter<'a>( + std::collections::btree_map::Values<'a, InternedStrCompareAsStr, DynPlatform>, +); + +impl<'a> Iterator for PlatformRegistryIter<'a> { + type Item = &'a DynPlatform; + + fn next(&mut self) -> Option { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + fn count(self) -> usize + where + Self: Sized, + { + self.0.count() + } + + fn last(self) -> Option { + self.0.last() + } + + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0.fold(init, f) + } +} + +impl<'a> std::iter::FusedIterator for PlatformRegistryIter<'a> {} + +impl<'a> ExactSizeIterator for PlatformRegistryIter<'a> {} + +impl<'a> DoubleEndedIterator for PlatformRegistryIter<'a> { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + + fn nth_back(&mut self, n: usize) -> Option { + self.0.nth_back(n) + } + + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0.rfold(init, f) + } +} + +#[derive(Clone, Debug)] +pub struct PlatformRegistryIterWithNames<'a>( + std::collections::btree_map::Iter<'a, InternedStrCompareAsStr, DynPlatform>, +); + +impl<'a> Iterator for PlatformRegistryIterWithNames<'a> { + type Item = (Interned, &'a DynPlatform); + + fn next(&mut self) -> Option { + self.0.next().map(|(name, platform)| (name.0, platform)) + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + fn count(self) -> usize + where + Self: Sized, + { + self.0.count() + } + + fn last(self) -> Option { + self.0.last().map(|(name, platform)| (name.0, platform)) + } + + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n).map(|(name, platform)| (name.0, platform)) + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0 + .map(|(name, platform)| (name.0, platform)) + .fold(init, f) + } +} + +impl<'a> std::iter::FusedIterator for PlatformRegistryIterWithNames<'a> {} + +impl<'a> ExactSizeIterator for PlatformRegistryIterWithNames<'a> {} + +impl<'a> DoubleEndedIterator for PlatformRegistryIterWithNames<'a> { + fn next_back(&mut self) -> Option { + self.0 + .next_back() + .map(|(name, platform)| (name.0, platform)) + } + + fn nth_back(&mut self, n: usize) -> Option { + self.0 + .nth_back(n) + .map(|(name, platform)| (name.0, platform)) + } + + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.0 + .map(|(name, platform)| (name.0, platform)) + .rfold(init, f) + } +} + +#[track_caller] +pub fn register_platform(kind: K) { + DynPlatform::new(kind).register(); +} + +impl Serialize for DynPlatform { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.name().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynPlatform { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let name = Cow::::deserialize(deserializer)?; + match Self::registry().get_by_name(&name) { + Some(retval) => Ok(retval.clone()), + None => Err(D::Error::custom(format_args!( + "unknown platform: name not found in registry: {name:?}" + ))), + } + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct DynPlatformValueParser; + +#[derive(Clone, PartialEq, Eq, Hash)] +struct DynPlatformValueEnum { + name: Interned, + platform: DynPlatform, +} + +impl clap::ValueEnum for DynPlatformValueEnum { + fn value_variants<'a>() -> &'a [Self] { + Interned::into_inner( + PlatformRegistrySnapshot::get() + .iter_with_names() + .map(|(name, platform)| Self { + name, + platform: platform.clone(), + }) + .collect(), + ) + } + + fn to_possible_value(&self) -> Option { + Some(clap::builder::PossibleValue::new(Interned::into_inner( + self.name, + ))) + } +} + +impl clap::builder::TypedValueParser for DynPlatformValueParser { + type Value = DynPlatform; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> clap::error::Result { + clap::builder::EnumValueParser::::new() + .parse_ref(cmd, arg, value) + .map(|v| v.platform) + } + + fn possible_values( + &self, + ) -> Option + '_>> { + static ENUM_VALUE_PARSER: OnceLock> = + OnceLock::new(); + ENUM_VALUE_PARSER + .get_or_init(clap::builder::EnumValueParser::::new) + .possible_values() + } +} + +impl clap::builder::ValueParserFactory for DynPlatform { + type Parser = DynPlatformValueParser; + + fn value_parser() -> Self::Parser { + DynPlatformValueParser::default() + } +} + +pub(crate) fn built_in_platforms() -> impl IntoIterator { + crate::vendor::built_in_platforms() +} diff --git a/crates/fayalite/src/platform/peripherals.rs b/crates/fayalite/src/platform/peripherals.rs new file mode 100644 index 00000000..387142de --- /dev/null +++ b/crates/fayalite/src/platform/peripherals.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::prelude::*; +use ordered_float::NotNan; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub struct ClockInputProperties { + pub frequency: NotNan, +} + +#[hdl(no_runtime_generics, no_static)] +pub struct ClockInput { + pub clk: Clock, + pub properties: PhantomConst, +} + +impl ClockInput { + #[track_caller] + pub fn new(frequency: f64) -> Self { + assert!( + frequency > 0.0 && frequency.is_finite(), + "invalid clock frequency: {frequency}" + ); + Self { + clk: Clock, + properties: PhantomConst::new_sized(ClockInputProperties { + frequency: NotNan::new(frequency).expect("just checked"), + }), + } + } + pub fn frequency(self) -> f64 { + self.properties.get().frequency.into_inner() + } +} + +#[hdl] +pub struct Led { + pub on: Bool, +} + +#[hdl] +pub struct RgbLed { + pub r: Bool, + pub g: Bool, + pub b: Bool, +} + +#[hdl] +/// UART, used as an output from the FPGA +pub struct Uart { + /// transmit from the FPGA's perspective + pub tx: Bool, + /// receive from the FPGA's perspective + #[hdl(flip)] + pub rx: Bool, +} diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 4fca2ad8..4c5bfdf4 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -7,13 +7,13 @@ pub use crate::{ DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, }, array::{Array, ArrayType}, + build::{BuildCli, JobParams, RunBuild}, bundle::Bundle, - cli::Cli, clock::{Clock, ClockDomain, ToClock}, enum_::{Enum, HdlNone, HdlOption, HdlSome}, expr::{ CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, - ReduceBits, ToExpr, repeat, + ReduceBits, ToExpr, ValueType, repeat, }, formal::{ MakeFormalExpr, all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, @@ -27,7 +27,8 @@ pub use crate::{ Instance, Module, ModuleBuilder, annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, memory_with_init, reg_builder, wire, }, - phantom_const::PhantomConst, + phantom_const::{PhantomConst, PhantomConstGet}, + platform::{DynPlatform, Platform, PlatformIOBuilder, peripherals}, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, sim::{ @@ -36,6 +37,7 @@ pub use crate::{ value::{SimOnly, SimOnlyValue, SimValue, ToSimValue, ToSimValueWithType}, }, source_location::SourceLocation, + testing::{FormalMode, assert_formal}, ty::{AsMask, CanonicalType, Type}, util::{ConstUsize, GenericConstUsize}, wire::Wire, diff --git a/crates/fayalite/src/reg.rs b/crates/fayalite/src/reg.rs index 20e0b944..7f506552 100644 --- a/crates/fayalite/src/reg.rs +++ b/crates/fayalite/src/reg.rs @@ -2,7 +2,7 @@ // See Notices.txt for copyright information use crate::{ clock::ClockDomain, - expr::{Expr, Flow}, + expr::{Expr, Flow, ValueType, value_category::ValueCategoryExpr}, intern::Interned, module::{NameId, ScopedNameId}, reset::{Reset, ResetType}, @@ -20,7 +20,16 @@ pub struct Reg { init: Option>, } -impl fmt::Debug for Reg { +impl ValueType for Reg { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl fmt::Debug for Reg { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { name, @@ -68,7 +77,7 @@ impl Reg { "register type must be a storable type" ); if let Some(init) = init { - assert_eq!(ty, Expr::ty(init), "register's type must match init type"); + assert_eq!(ty, init.ty(), "register's type must match init type"); } Self { name: scoped_name, @@ -78,9 +87,6 @@ impl Reg { init, } } - pub fn ty(&self) -> T { - self.ty - } pub fn source_location(&self) -> SourceLocation { self.source_location } diff --git a/crates/fayalite/src/reset.rs b/crates/fayalite/src/reset.rs index 5dff2784..13273ac1 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -2,13 +2,15 @@ // See Notices.txt for copyright information use crate::{ clock::Clock, - expr::{Expr, ToExpr, ops}, - int::{Bool, SInt, UInt}, + expr::{CastToImpl, Expr, ValueType}, + int::{Bool, SInt, SIntValue, UInt, UIntValue}, + sim::value::SimValue, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, }, + util::ConstUsize, }; use bitvec::{bits, order::Lsb0}; @@ -19,15 +21,15 @@ mod sealed { pub trait ResetType: StaticType + sealed::ResetTypeSealed - + ops::ExprCastTo - + ops::ExprCastTo - + ops::ExprCastTo - + ops::ExprCastTo - + ops::ExprCastTo - + ops::ExprCastTo> - + ops::ExprCastTo> - + ops::ExprCastTo - + ops::ExprCastTo + + CastToImpl + + CastToImpl> + + CastToImpl> + + CastToImpl> + + CastToImpl> + + CastToImpl, ValueOutput = UIntValue>> + + CastToImpl, ValueOutput = SIntValue>> + + CastToImpl + + CastToImpl { fn dispatch(input: D::Input, dispatch: D) -> D::Output; } @@ -132,29 +134,34 @@ macro_rules! reset_type { } pub trait $Trait { - fn $trait_fn(&self) -> Expr<$name>; + type Output: ValueType; + fn $trait_fn(&self) -> Self::Output; } impl $Trait for &'_ T { - fn $trait_fn(&self) -> Expr<$name> { + type Output = T::Output; + fn $trait_fn(&self) -> Self::Output { (**self).$trait_fn() } } impl $Trait for &'_ mut T { - fn $trait_fn(&self) -> Expr<$name> { + type Output = T::Output; + fn $trait_fn(&self) -> Self::Output { (**self).$trait_fn() } } impl $Trait for Box { - fn $trait_fn(&self) -> Expr<$name> { + type Output = T::Output; + fn $trait_fn(&self) -> Self::Output { (**self).$trait_fn() } } $($impl_trait $Trait for Expr<$name> { - fn $trait_fn(&self) -> Expr<$name> { + type Output = Expr<$name>; + fn $trait_fn(&self) -> Self::Output { *self } })? @@ -171,13 +178,15 @@ reset_type!( ); impl ToSyncReset for bool { - fn to_sync_reset(&self) -> Expr { - self.to_expr().to_sync_reset() + type Output = SimValue; + fn to_sync_reset(&self) -> Self::Output { + SimValue::from_value(SyncReset, *self) } } impl ToAsyncReset for bool { - fn to_async_reset(&self) -> Expr { - self.to_expr().to_async_reset() + type Output = SimValue; + fn to_async_reset(&self) -> Self::Output { + SimValue::from_value(AsyncReset, *self) } } diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 1717aec6..7068e446 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -6,13 +6,15 @@ use crate::{ bundle::{BundleField, BundleType}, expr::{ - Flow, ToLiteralBits, + Flow, target::{ GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, }, int::BoolOrIntType, - intern::{Intern, Interned, InternedCompare, PtrEqWithTypeId, SupportsPtrEqWithTypeId}, + intern::{ + Intern, InternSlice, Interned, InternedCompare, PtrEqWithTypeId, SupportsPtrEqWithTypeId, + }, module::{ ModuleIO, transform::visit::{Fold, Folder, Visit, Visitor}, @@ -22,7 +24,7 @@ use crate::{ sim::{ compiler::{ Compiled, CompiledBundleField, CompiledExternModule, CompiledTypeLayoutBody, - CompiledValue, + CompiledValue, ExternModuleClockForPast, }, interpreter::{ BreakAction, BreakpointsSet, RunResult, SmallUInt, State, @@ -45,18 +47,18 @@ use num_bigint::BigInt; use num_traits::{Signed, Zero}; use std::{ any::Any, - borrow::Cow, - cell::RefCell, - collections::BTreeMap, + cell::{Cell, RefCell}, + collections::{BTreeMap, BTreeSet}, fmt, future::{Future, IntoFuture}, hash::Hash, mem, - pin::Pin, + pin::{Pin, pin}, ptr, rc::Rc, - sync::Arc, + sync::{Arc, Mutex, MutexGuard}, task::Poll, + usize, }; pub mod compiler; @@ -262,7 +264,7 @@ impl_trace_decl! { }), Instance(TraceInstance { fn children(self) -> _ { - [self.instance_io.into(), self.module.into()][..].intern() + [self.instance_io.into(), self.module.into()].intern_slice() } name: Interned, instance_io: TraceBundle, @@ -282,7 +284,7 @@ impl_trace_decl! { }), MemPort(TraceMemPort { fn children(self) -> _ { - [self.bundle.into()][..].intern() + [self.bundle.into()].intern_slice() } name: Interned, bundle: TraceBundle, @@ -290,7 +292,7 @@ impl_trace_decl! { }), Wire(TraceWire { fn children(self) -> _ { - [*self.child][..].intern() + [*self.child].intern_slice() } name: Interned, child: Interned, @@ -298,7 +300,7 @@ impl_trace_decl! { }), Reg(TraceReg { fn children(self) -> _ { - [*self.child][..].intern() + [*self.child].intern_slice() } name: Interned, child: Interned, @@ -306,7 +308,7 @@ impl_trace_decl! { }), ModuleIO(TraceModuleIO { fn children(self) -> _ { - [*self.child][..].intern() + [*self.child].intern_slice() } name: Interned, child: Interned, @@ -413,6 +415,15 @@ impl_trace_decl! { name: Interned, flow: Flow, }), + PhantomConst(TracePhantomConst { + fn location(self) -> _ { + self.location + } + location: TraceLocation, + name: Interned, + ty: PhantomConst, + flow: Flow, + }), SimOnly(TraceSimOnly { fn location(self) -> _ { self.location @@ -523,6 +534,11 @@ pub trait TraceWriter: fmt::Debug + 'static { variant_index: usize, ty: Enum, ) -> Result<(), Self::Error>; + fn set_signal_phantom_const( + &mut self, + id: TraceScalarId, + ty: PhantomConst, + ) -> Result<(), Self::Error>; fn set_signal_sim_only_value( &mut self, id: TraceScalarId, @@ -582,6 +598,11 @@ trait TraceWriterDynTrait: fmt::Debug + 'static { variant_index: usize, ty: Enum, ) -> std::io::Result<()>; + fn set_signal_phantom_const_dyn( + &mut self, + id: TraceScalarId, + ty: PhantomConst, + ) -> std::io::Result<()>; fn set_signal_sim_only_value_dyn( &mut self, id: TraceScalarId, @@ -646,6 +667,13 @@ impl TraceWriterDynTrait for T { .map_err(err_into_io)?, ) } + fn set_signal_phantom_const_dyn( + &mut self, + id: TraceScalarId, + ty: PhantomConst, + ) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_phantom_const(self, id, ty).map_err(err_into_io)?) + } fn set_signal_sim_only_value_dyn( &mut self, id: TraceScalarId, @@ -717,6 +745,13 @@ impl TraceWriter for DynTraceWriter { self.0 .set_signal_enum_discriminant_dyn(id, variant_index, ty) } + fn set_signal_phantom_const( + &mut self, + id: TraceScalarId, + ty: PhantomConst, + ) -> Result<(), Self::Error> { + self.0.set_signal_phantom_const_dyn(id, ty) + } fn set_signal_sim_only_value( &mut self, id: TraceScalarId, @@ -892,12 +927,16 @@ pub(crate) enum SimTraceKind { index: StatePartIndex, ty: DynSimOnly, }, + PhantomConst { + ty: PhantomConst, + }, } #[derive(PartialEq, Eq)] pub(crate) enum SimTraceState { Bits(BitVec), SimOnly(DynSimOnlyValue), + PhantomConst, } impl Clone for SimTraceState { @@ -905,6 +944,7 @@ impl Clone for SimTraceState { match self { Self::Bits(v) => Self::Bits(v.clone()), Self::SimOnly(v) => Self::SimOnly(v.clone()), + Self::PhantomConst => Self::PhantomConst, } } fn clone_from(&mut self, source: &Self) { @@ -953,6 +993,7 @@ impl fmt::Debug for SimTraceState { match self { SimTraceState::Bits(v) => BitSliceWriteWithBase(v).fmt(f), SimTraceState::SimOnly(v) => v.fmt(f), + SimTraceState::PhantomConst => f.debug_tuple("PhantomConst").finish(), } } } @@ -979,6 +1020,7 @@ impl SimTraceKind { SimTraceKind::EnumDiscriminant { index: _, ty } => { SimTraceState::Bits(BitVec::repeat(false, ty.discriminant_bit_width())) } + SimTraceKind::PhantomConst { .. } => SimTraceState::PhantomConst, SimTraceKind::SimOnly { index: _, ty } => SimTraceState::SimOnly(ty.default_value()), } } @@ -997,6 +1039,12 @@ impl MaybeNeedsSettle { MaybeNeedsSettle::NoSettleNeeded(v) => MaybeNeedsSettle::NoSettleNeeded(f(v)), } } + fn into_inner(self) -> T { + match self { + MaybeNeedsSettle::NeedsSettle(v) => v, + MaybeNeedsSettle::NoSettleNeeded(v) => v, + } + } } // workaround implementing FnOnce not being stable @@ -1031,6 +1079,7 @@ struct SimulationModuleState { uninitialized_ios: HashMap>, io_targets: HashMap>, did_initial_settle: bool, + clocks_for_past: HashMap, SimulationExternModuleClockForPast>, } impl fmt::Debug for SimulationModuleState { @@ -1040,23 +1089,37 @@ impl fmt::Debug for SimulationModuleState { uninitialized_ios, io_targets, did_initial_settle, + clocks_for_past, } = 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) + .field("clocks_for_past", &SortedMapDebug(clocks_for_past)) .finish() } } impl SimulationModuleState { - fn new(base_targets: impl IntoIterator)>) -> Self { + fn new( + base_targets: impl IntoIterator)>, + clocks_for_past: &[ExternModuleClockForPast], + ) -> Self { let mut retval = Self { base_targets: Vec::new(), uninitialized_ios: HashMap::default(), io_targets: HashMap::default(), did_initial_settle: false, + clocks_for_past: clocks_for_past + .iter() + .map(|clock_for_past| { + ( + clock_for_past.clock_for_past, + SimulationExternModuleClockForPast::new(clock_for_past), + ) + }) + .collect(), }; for (base_target, value) in base_targets { retval.base_targets.push(base_target); @@ -1094,6 +1157,7 @@ impl SimulationModuleState { true } } + CompiledTypeLayoutBody::PhantomConst => false, CompiledTypeLayoutBody::Bundle { .. } => { let value = value.map_ty(Bundle::from_canonical); let mut sub_targets = Vec::new(); @@ -1184,7 +1248,34 @@ impl SimulationModuleState { } } #[track_caller] - fn read_helper( + fn is_reset_async(&self, io: Expr, which_module: WhichModule) -> bool { + 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" + ), + } + }; + match self.get_io(*target, which_module).layout.ty { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::Array(_) + | CanonicalType::Enum(_) + | CanonicalType::Bundle(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => unreachable!(), + CanonicalType::AsyncReset(_) => true, + CanonicalType::SyncReset(_) => false, + } + } + #[track_caller] + fn read_helper_current( &self, io: Expr, which_module: WhichModule, @@ -1229,6 +1320,33 @@ impl SimulationModuleState { } } #[track_caller] + fn read_helper( + &self, + io: Expr, + read_time: ReadTime, + which_module: WhichModule, + ) -> MaybeNeedsSettle> { + match read_time { + ReadTime::Current => self.read_helper_current(io, which_module), + ReadTime::Past { clock_for_past } => { + let current = self.read_helper_current(io, which_module); + let clock_for_past_value = self + .read_helper_current(Expr::canonical(clock_for_past), which_module) + .into_inner() + .map_ty(Clock::from_canonical); + let Some(clock_for_past) = self.clocks_for_past.get(&clock_for_past_value) else { + panic!( + "In order to use the `read_past()` family of functions,\n\ + you must call `m.register_clock_for_past(my_io.clk)`\n\ + in the module's body for every clock you use as the\n\ + second argument of the `read_past()` family." + ); + }; + current.map(|current| clock_for_past.current_to_past_map[¤t]) + } + } + } + #[track_caller] fn write_helper( &mut self, io: Expr, @@ -1260,149 +1378,71 @@ impl SimulationModuleState { } } -#[derive(Copy, Clone, Debug)] -enum WaitTarget { - Settle, - Instant(SimInstant), - Change { key: ChangeKey, value: ChangeValue }, +struct SimulationExternModuleClockForPast { + current_to_past_map: HashMap, CompiledValue>, } -#[derive(Clone)] -struct EarliestWaitTargets { - settle: bool, - instant: Option, - changes: HashMap, SimValue>, -} - -impl fmt::Debug for EarliestWaitTargets { +impl fmt::Debug for SimulationExternModuleClockForPast { 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, + current_to_past_map, } = self; - *settle = false; - *instant = None; - changes.clear(); + f.debug_struct("SimulationExternModuleClockForPast") + .field("current_to_past_map", &SortedMapDebug(current_to_past_map)) + .finish() } - 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()); - } +} + +impl SimulationExternModuleClockForPast { + fn new(clock_for_past: &ExternModuleClockForPast) -> Self { + let mut retval = Self { + current_to_past_map: HashMap::default(), + }; + for (current, past) in clock_for_past.current_to_past_map { + retval.add_current_to_past_mapping(current, past); } - } - 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 } + fn add_current_to_past_mapping( + &mut self, + current: CompiledValue, + past: CompiledValue, + ) { + self.current_to_past_map.insert(current, past); + match current.layout.body { + CompiledTypeLayoutBody::Scalar | CompiledTypeLayoutBody::PhantomConst => {} + CompiledTypeLayoutBody::Array { .. } => { + let current = current.map_ty(Array::from_canonical); + let past = past.map_ty(Array::from_canonical); + for index in 0..current.layout.ty.len() { + self.add_current_to_past_mapping(current.element(index), past.element(index)); + } + } + CompiledTypeLayoutBody::Bundle { .. } => { + let current = current.map_ty(Bundle::from_canonical); + let past = past.map_ty(Bundle::from_canonical); + for BundleField { name, .. } in current.layout.ty.fields() { + self.add_current_to_past_mapping( + current.field_by_name(name), + past.field_by_name(name), + ); + } + } + } + } +} + +enum ReadTime { + Current, + Past { clock_for_past: Expr }, } struct SimulationExternModuleState { module_state: SimulationModuleState, sim: ExternModuleSimulation, running_generator: Option + 'static>>>, - wait_targets: EarliestWaitTargets, + waker: Arc, + debug_name: Interned, } impl fmt::Debug for SimulationExternModuleState { @@ -1411,7 +1451,8 @@ impl fmt::Debug for SimulationExternModuleState { module_state, sim, running_generator, - wait_targets, + waker: _, + debug_name: _, } = self; f.debug_struct("SimulationExternModuleState") .field("module_state", module_state) @@ -1420,7 +1461,6 @@ impl fmt::Debug for SimulationExternModuleState { "running_generator", &running_generator.as_ref().map(|_| DebugAsDisplay("...")), ) - .field("wait_targets", wait_targets) .finish() } } @@ -1463,9 +1503,10 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo 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) + Some(TypeLenSingle::SmallSlot) => io + .ty() .value_from_int_wrapping(state.small_slots[compiled_value.range.small_slots.start]), - Some(TypeLenSingle::BigSlot) => Expr::ty(io).value_from_int_wrapping( + Some(TypeLenSingle::BigSlot) => io.ty().value_from_int_wrapping( state.big_slots[compiled_value.range.big_slots.start].clone(), ), Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), @@ -1487,28 +1528,350 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { } } -struct GeneratorWaker; +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum EventKind { + State, + ExternModule(usize), +} -impl std::task::Wake for GeneratorWaker { - fn wake(self: Arc) { - panic!("can't await other kinds of futures in function passed to ExternalModuleSimulation"); +impl PartialOrd for EventKind { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } -#[derive(Default)] -struct ReadyToRunSet { - state_ready_to_run: bool, - extern_modules_ready_to_run: Vec, +impl Ord for EventKind { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match (self, other) { + (Self::State, Self::State) => std::cmp::Ordering::Equal, + (Self::State, Self::ExternModule(_)) => std::cmp::Ordering::Less, + (Self::ExternModule(_), Self::State) => std::cmp::Ordering::Greater, + (Self::ExternModule(this), Self::ExternModule(other)) => this.cmp(other), + } + } } -impl ReadyToRunSet { - fn clear(&mut self) { +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +struct Event { + instant: SimInstant, + kind: EventKind, +} + +impl PartialOrd for Event { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Event { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let Self { instant, kind } = other; + self.instant.cmp(instant).then(self.kind.cmp(kind)) + } +} + +struct HashableWaker(std::task::Waker); + +impl Clone for HashableWaker { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + + fn clone_from(&mut self, source: &Self) { + self.0.clone_from(&source.0); + } +} + +impl PartialEq for HashableWaker { + fn eq(&self, other: &Self) -> bool { + self.0.will_wake(&other.0) + } +} + +impl Eq for HashableWaker {} + +impl Hash for HashableWaker { + fn hash(&self, state: &mut H) { + self.0.data().hash(state); + let vtable: *const std::task::RawWakerVTable = self.0.vtable(); + vtable.hash(state); + } +} + +enum EventWakers { + Wakers(HashSet), + CurrentlyWaking, +} + +impl EventWakers { + fn start_waking(&mut self) -> HashSet { + match std::mem::replace(self, Self::CurrentlyWaking) { + Self::Wakers(retval) => retval, + Self::CurrentlyWaking => unreachable!(), + } + } +} + +impl fmt::Debug for EventWakers { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Wakers(wakers) => f.debug_tuple("Wakers").field(&wakers.len()).finish(), + Self::CurrentlyWaking => write!(f, "CurrentlyWaking"), + } + } +} + +struct EventQueueData { + instant: SimInstant, + events: BTreeMap, + trace: bool, +} + +impl fmt::Debug for EventQueueData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { - state_ready_to_run, - extern_modules_ready_to_run, + instant, + events, + trace: _, } = self; - *state_ready_to_run = false; - extern_modules_ready_to_run.clear(); + f.debug_struct("EventQueueData") + .field("instant", instant) + .field("events", events) + .finish() + } +} + +struct EventQueueFirstEvent<'a> { + event_queue: Arc, + event_queue_lock: MutexGuard<'a, EventQueueData>, +} + +impl<'a> EventQueueFirstEvent<'a> { + fn event(&self) -> &Event { + self.event_queue_lock + .events + .first_key_value() + .expect("known to be non-empty") + .0 + } + fn remove_and_wake_all_wakers(self) { + let Self { + event_queue, + mut event_queue_lock, + } = self; + let trace = event_queue_lock.trace; + let mut entry = event_queue_lock + .events + .first_entry() + .expect("known to be non-empty"); + let wakers = entry.get_mut().start_waking(); + let event = *entry.key(); + if wakers.is_empty() { + entry.remove(); + if trace { + println!( + "EventQueue first_event remove_and_wake_all_wakers(): event={event:?} no wakers", + ); + } + drop(event_queue_lock); + return; + } + drop(event_queue_lock); + if trace { + println!( + "EventQueue first_event remove_and_wake_all_wakers(): event={event:?} wakers_len={}", + wakers.len() + ); + } + for waker in wakers { + waker.0.wake(); + } + event_queue.lock().events.remove(&event); + if trace { + println!( + "EventQueue first_event remove_and_wake_all_wakers(): event={event:?} finished waking", + ); + } + } + fn into_event_queue_lock(self) -> MutexGuard<'a, EventQueueData> { + self.event_queue_lock + } +} + +impl EventQueueData { + fn new(instant: SimInstant, trace: bool) -> Self { + Self { + instant, + events: BTreeMap::new(), + trace, + } + } + fn instant(&self) -> SimInstant { + self.instant + } + fn set_instant(&mut self, instant: SimInstant) { + if self.trace { + println!("EventQueue set_instant({instant:?})"); + } + self.instant = instant; + } + fn add_event( + &mut self, + mut event: Event, + waker: Option, + ) -> Result<(), std::task::Waker> { + event.instant = event.instant.max(self.instant); + let wakers = self + .events + .entry(event) + .or_insert_with(|| EventWakers::Wakers(HashSet::default())); + let EventWakers::Wakers(wakers) = wakers else { + return match waker { + Some(waker) => Err(waker), + None => Ok(()), + }; + }; + if self.trace { + println!( + "EventQueue add_event({event:?}, {:?})", + waker.is_some().then_some(format_args!("")) + ); + } + wakers.extend(waker.map(HashableWaker)); + Ok(()) + } + fn add_event_for_now(&mut self, kind: EventKind) { + self.add_event( + Event { + instant: self.instant, + kind, + }, + None, + ) + .expect("no waker passed in") + } + fn first_event<'a>( + this: MutexGuard<'a, EventQueueData>, + event_queue: Arc, + ) -> Result, MutexGuard<'a, EventQueueData>> { + if this.events.is_empty() { + Err(this) + } else { + Ok(EventQueueFirstEvent { + event_queue, + event_queue_lock: this, + }) + } + } + fn peek_first_event(&self) -> Option { + Some(*self.events.first_key_value()?.0) + } + fn peek_first_event_for_now(&self) -> Option { + self.peek_first_event() + .filter(|event| event.instant <= self.instant) + } +} + +struct EventQueue(Mutex); + +impl fmt::Debug for EventQueue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("EventQueue(")?; + if let Ok(data) = self.0.try_lock() { + (*data).fmt(f)?; + } else { + f.write_str("")?; + } + f.write_str(")") + } +} + +impl EventQueue { + fn new(data: EventQueueData) -> Self { + Self(Mutex::new(data)) + } + fn lock(&self) -> MutexGuard<'_, EventQueueData> { + self.0.lock().expect("not poisoned") + } + fn add_event_for_now(&self, kind: EventKind) { + self.lock().add_event_for_now(kind); + } + fn peek_first_event_for_now(&self) -> Option { + self.lock().peek_first_event_for_now() + } +} + +struct ExternModuleGeneratorWaker { + event_queue: std::sync::Weak, + module_index: usize, +} + +impl std::task::Wake for ExternModuleGeneratorWaker { + fn wake(self: Arc) { + self.wake_by_ref(); + } + fn wake_by_ref(self: &Arc) { + if let Some(event_queue) = self.event_queue.upgrade() { + event_queue.add_event_for_now(EventKind::ExternModule(self.module_index)); + } + } +} + +struct SensitivitySet { + debug_id: u64, + compiled_values: HashSet>>, + values: Vec<( + Rc>, + Rc>, + )>, + waker: RefCell, + changed: Cell, +} + +struct SensitivitySetJustId<'a>(&'a SensitivitySet); + +impl fmt::Debug for SensitivitySetJustId<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.debug_fmt(true, f) + } +} + +impl fmt::Debug for SensitivitySet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt(false, f) + } +} + +impl SensitivitySet { + fn debug_fmt(&self, just_id: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + debug_id, + compiled_values: _, + values, + waker: _, + changed, + } = self; + struct DebugValues<'a>( + &'a [( + Rc>, + Rc>, + )], + ); + impl<'a> fmt::Debug for DebugValues<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map() + .entries(self.0.iter().map(|(k, v)| (k, v))) + .finish() + } + } + let mut debug_struct = f.debug_struct("SensitivitySet"); + debug_struct.field("id", debug_id); + if !just_id { + debug_struct + .field("values", &DebugValues(values)) + .field("changed", changed); + } + debug_struct.finish_non_exhaustive() } } @@ -1517,15 +1880,22 @@ struct SimulationImpl { io: Expr, main_module: SimulationModuleState, extern_modules: Box<[SimulationExternModuleState]>, - state_ready_to_run: bool, trace_decls: TraceModule, traces: SimTraces]>>, - trace_memories: HashMap, TraceMem>, + trace_memories: BTreeMap, TraceMem>, trace_writers: Vec>, - instant: SimInstant, clocks_triggered: Interned<[StatePartIndex]>, breakpoints: Option, - generator_waker: std::task::Waker, + event_queue: Arc, + next_sensitivity_set_debug_id: u64, + waiting_sensitivity_sets_by_compiled_value: HashMap< + Rc>, + ( + Rc>, + HashMap<*const SensitivitySet, Rc>, + ), + >, + waiting_sensitivity_sets_by_address: HashMap<*const SensitivitySet, Rc>, } impl fmt::Debug for SimulationImpl { @@ -1534,6 +1904,70 @@ impl fmt::Debug for SimulationImpl { } } +struct DebugSensitivitySetsByAddress<'a> { + sensitivity_sets: &'a HashMap<*const SensitivitySet, Rc>, + just_id: bool, +} + +impl<'a> fmt::Debug for DebugSensitivitySetsByAddress<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + sensitivity_sets, + just_id, + } = *self; + let mut values: Vec<_> = sensitivity_sets.values().collect(); + values.sort_by_key(|v| v.debug_id); + if just_id { + f.debug_set() + .entries( + values + .iter() + .map(|sensitivity_set| SensitivitySetJustId(sensitivity_set)), + ) + .finish() + } else { + f.debug_set().entries(values).finish() + } + } +} + +struct DebugSensitivitySetsByCompiledValue<'a>( + &'a HashMap< + Rc>, + ( + Rc>, + HashMap<*const SensitivitySet, Rc>, + ), + >, +); + +impl<'a> fmt::Debug for DebugSensitivitySetsByCompiledValue<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut values: Vec<_> = self + .0 + .iter() + .map(|(compiled_value, (sim_value, sensitivity_sets))| { + ( + DebugAsDisplay(if f.alternate() { + format!("{compiled_value:#?}") + } else { + format!("{compiled_value:?}") + }), + ( + sim_value, + DebugSensitivitySetsByAddress { + sensitivity_sets, + just_id: true, + }, + ), + ) + }) + .collect(); + values.sort_by(|l, r| l.0.0.cmp(&r.0.0)); + f.debug_map().entries(values).finish() + } +} + impl SimulationImpl { fn debug_fmt(&self, io: Option<&dyn fmt::Debug>, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { @@ -1541,48 +1975,75 @@ impl SimulationImpl { io: self_io, main_module, extern_modules, - state_ready_to_run, trace_decls, traces, trace_memories, trace_writers, - instant, clocks_triggered, breakpoints: _, - generator_waker: _, + event_queue, + next_sensitivity_set_debug_id: _, + waiting_sensitivity_sets_by_compiled_value, + waiting_sensitivity_sets_by_address, } = 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) + .field("event_queue", event_queue) + .field( + "waiting_sensitivity_sets_by_address", + &DebugSensitivitySetsByAddress { + sensitivity_sets: waiting_sensitivity_sets_by_address, + just_id: false, + }, + ) + .field( + "waiting_sensitivity_sets_by_compiled_value", + &DebugSensitivitySetsByCompiledValue(waiting_sensitivity_sets_by_compiled_value), + ) .finish_non_exhaustive() } fn new(compiled: Compiled) -> 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, - }| { + let mut event_queue = EventQueueData::new(SimInstant::START, false); + event_queue.add_event_for_now(EventKind::State); + for module_index in 0..compiled.extern_modules.len() { + event_queue.add_event_for_now(EventKind::ExternModule(module_index)); + } + let event_queue = Arc::new(EventQueue::new(event_queue)); + let extern_modules = Box::from_iter(compiled.extern_modules.iter().enumerate().map( + |( + module_index, + &CompiledExternModule { + module_io_targets, + module_io, + clocks_for_past, + simulation, + debug_name, + }, + )| { SimulationExternModuleState { module_state: SimulationModuleState::new( module_io_targets .iter() .copied() .zip(module_io.iter().copied()), + &clocks_for_past, ), sim: simulation, running_generator: None, - wait_targets: EarliestWaitTargets::settle(), + waker: Arc::new(ExternModuleGeneratorWaker { + event_queue: Arc::downgrade(&event_queue), + module_index, + }), + debug_name, } }, )); @@ -1605,9 +2066,9 @@ impl SimulationImpl { 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 { @@ -1620,12 +2081,14 @@ impl SimulationImpl { last_state: kind.make_state(), }, ))), - trace_memories: HashMap::from_iter(compiled.trace_memories.iter().copied()), + trace_memories: BTreeMap::from_iter(compiled.trace_memories.iter().copied()), trace_writers: vec![], - instant: SimInstant::START, clocks_triggered: compiled.clocks_triggered, breakpoints: None, - generator_waker: Arc::new(GeneratorWaker).into(), + event_queue, + next_sensitivity_set_debug_id: 0, + waiting_sensitivity_sets_by_compiled_value: HashMap::default(), + waiting_sensitivity_sets_by_address: HashMap::default(), } } fn write_traces( @@ -1697,6 +2160,9 @@ impl SimulationImpl { ty, )?; } + SimTraceKind::PhantomConst { ty } => { + trace_writer.set_signal_phantom_const(id, ty)? + } SimTraceKind::SimOnly { .. } => { trace_writer.set_signal_sim_only_value(id, state.unwrap_sim_only_ref())? } @@ -1767,6 +2233,7 @@ impl SimulationImpl { .unwrap_bits_mut() .set(0, self.state.small_slots[index] != 0); } + SimTraceKind::PhantomConst { .. } => {} SimTraceKind::SimOnly { index, ty: _ } => { state .unwrap_sim_only_mut() @@ -1780,137 +2247,198 @@ impl SimulationImpl { } #[track_caller] fn advance_time(this_ref: &Rc>, duration: SimDuration) { - let run_target = this_ref.borrow().instant + duration; - Self::run_until(this_ref, run_target); + Self::run_until(this_ref, &mut |instant| instant.checked_add(duration)); } - /// 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, + fn wake_after_change(&mut self, sensitivity_set: Rc) { + let None = self + .waiting_sensitivity_sets_by_address + .insert(Rc::as_ptr(&sensitivity_set), sensitivity_set.clone()) + else { + // already added + return; + }; + if self.breakpoints.as_ref().is_some_and(|v| v.trace) { + println!("SimulationImpl::wake_after_change:\n{sensitivity_set:#?}"); } - 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; + let SensitivitySet { + debug_id: _, + compiled_values: _, + values, + waker: _, + changed: _, + } = &*sensitivity_set; + for (compiled_value, sim_value) in values { + self.waiting_sensitivity_sets_by_compiled_value + .entry(compiled_value.clone()) + .or_insert_with(|| (sim_value.clone(), HashMap::default())) + .1 + .insert(Rc::as_ptr(&sensitivity_set), sensitivity_set.clone()); + } + } + fn cancel_wake_after_change(&mut self, sensitivity_set: &Rc) { + let Some(_) = self + .waiting_sensitivity_sets_by_address + .remove(&Rc::as_ptr(&sensitivity_set)) + else { + return; + }; + if self.breakpoints.as_ref().is_some_and(|v| v.trace) { + println!("SimulationImpl::cancel_wake_after_change:\n{sensitivity_set:#?}"); + } + let SensitivitySet { + debug_id: _, + compiled_values: _, + values, + waker: _, + changed: _, + } = &**sensitivity_set; + for (compiled_value, _) in values { + if let hashbrown::hash_map::Entry::Occupied(mut entry) = self + .waiting_sensitivity_sets_by_compiled_value + .entry(compiled_value.clone()) + { + entry.get_mut().1.remove(&Rc::as_ptr(&sensitivity_set)); + if entry.get().1.is_empty() { + entry.remove(); } - 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, + } + fn try_wake_at_instant( + &mut self, + module_index: usize, + instant: &mut dyn FnMut(SimInstant) -> Option, + waker: std::task::Waker, + ) -> Result<(), std::task::Waker> { + let mut event_queue = self.event_queue.lock(); + let Some(instant) = instant(event_queue.instant()) else { + return Err(waker); + }; + match event_queue.add_event( + Event { + instant, + kind: EventKind::ExternModule(module_index), + }, + Some(waker), + ) { + Ok(()) => {} + Err(waker) => { + drop(event_queue); + waker.wake(); + } } + Ok(()) } async fn yield_advance_time_or_settle( - this: Rc>, + this: &RefCell, 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; + let mut target_instant = None; + std::future::poll_fn(|cx| { + match this.borrow_mut().try_wake_at_instant( + module_index, + &mut |instant| match target_instant { + Some(target_instant) => { + // already waited at least once + if instant < target_instant { + Some(target_instant) + } else { + None + } + } + None => { + target_instant = instant.checked_add(duration.unwrap_or(SimDuration::ZERO)); + target_instant + } + }, + cx.waker().clone(), + ) { + Ok(()) => Poll::Pending, + Err(_waker) => { + if target_instant.is_none() { + // don't panic in try_wait_at_instant to avoid poisoning the lock + panic!("SimInstant overflow"); + } + Poll::Ready(()) + } + } + }) + .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 + async fn yield_wait_for_changes( + this: &RefCell, + module_index: usize, + sensitivity_set: SensitivitySet, + timeout: Option, + ) { + if let Some(timeout) = timeout { + if timeout == SimDuration::ZERO { + return Self::yield_advance_time_or_settle(this, module_index, None).await; + } + } + let sensitivity_set = Rc::new(sensitivity_set); + struct CancelOnDrop<'a> { + this: &'a RefCell, + sensitivity_set: &'a Rc, + } + impl Drop for CancelOnDrop<'_> { + fn drop(&mut self) { + let Self { + this, + sensitivity_set, + } = self; + this.borrow_mut().cancel_wake_after_change(&sensitivity_set); + } + } + let _cancel_on_drop = CancelOnDrop { + this, + sensitivity_set: &sensitivity_set, + }; + let mut timeout_instant = None; + std::future::poll_fn(|cx| { + if sensitivity_set.changed.get() { + return Poll::Ready(()); + } + sensitivity_set.waker.borrow_mut().clone_from(cx.waker()); + let mut this = this.borrow_mut(); + this.wake_after_change(sensitivity_set.clone()); + if let Some(timeout) = timeout { + match this.try_wake_at_instant( + module_index, + &mut |instant| match timeout_instant { + Some(timeout_instant) => { + if instant < timeout_instant { + Some(timeout_instant) + } else { + None + } + } + None => { + timeout_instant = instant.checked_add(timeout); + timeout_instant + } + }, + cx.waker().clone(), + ) { + Ok(()) => Poll::Pending, + Err(_waker) => { + if timeout_instant.is_none() { + // don't panic in try_wait_at_instant to avoid poisoning the lock + panic!("SimInstant overflow"); + } + Poll::Ready(()) } } - }; - 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); + Poll::Pending } - 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!(), - TraceWriterState::Running(trace_writer) => { - trace_writer.change_time_to(this.instant)?; - } - TraceWriterState::Errored(_) => {} - } - Ok(trace_writer_state) - }); + }) + .await; } #[must_use] #[track_caller] - fn run_state_settle_cycle(&mut self) -> bool { - self.state_ready_to_run = false; + fn run_state_settle_cycle(&mut self) { self.state.setup_call(0); if self.breakpoints.is_some() { loop { @@ -1941,138 +2469,213 @@ impl SimulationImpl { .iter() .any(|i| self.state.small_slots[*i] != 0) { - self.state_ready_to_run = true; - true - } else { - false + if self.breakpoints.as_ref().is_some_and(|v| v.trace) { + println!("SimulationImpl clocks triggered"); + } + self.event_queue.add_event_for_now(EventKind::State); } } #[track_caller] - fn run_extern_modules_cycle( + fn run_extern_module(this_ref: &Rc>, module_index: usize) { + let mut this = this_ref.borrow_mut(); + let trace = this.breakpoints.as_ref().is_some_and(|v| v.trace); + let extern_module = &mut this.extern_modules[module_index]; + let debug_name = extern_module.debug_name; + let waker = std::task::Waker::from(extern_module.waker.clone()); + let mut generator = if !extern_module.module_state.did_initial_settle { + let sim = extern_module.sim; + drop(this); + if trace { + println!("{debug_name}: start"); + } + Box::into_pin(sim.run(ExternModuleSimulationState { + sim_impl: this_ref.clone(), + module_index, + })) + } else if let Some(generator) = extern_module.running_generator.take() { + drop(this); + generator + } else { + return; + }; + if trace { + println!("{debug_name}: poll"); + } + let poll_result = generator + .as_mut() + .poll(&mut std::task::Context::from_waker(&waker)); + let generator = match poll_result { + Poll::Ready(()) => None, + Poll::Pending => Some(generator), + }; + if trace { + println!("{debug_name}: poll returned {poll_result:?}"); + } + 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; + } + fn check_waiting_sensitivity_sets(&mut self) { + if let Some(Event { + instant: _, + kind: EventKind::State, + }) = self.event_queue.peek_first_event_for_now() + { + return; + } + let mut triggered = HashMap::<*const SensitivitySet, Rc>::default(); + for (compiled_value, (sim_value, sensitivity_sets)) in + &self.waiting_sensitivity_sets_by_compiled_value + { + if Self::value_changed( + &mut self.state, + **compiled_value, + SimValue::opaque(sim_value), + ) { + triggered.extend(sensitivity_sets.iter().map(|(&k, v)| (k, v.clone()))); + } + } + for sensitivity_set in triggered.into_values() { + sensitivity_set.changed.set(true); + sensitivity_set.waker.borrow().wake_by_ref(); + self.cancel_wake_after_change(&sensitivity_set); + } + } + fn write_traces_after_event(&mut self) { + if let Some(Event { + instant: _, + kind: EventKind::State, + }) = self.event_queue.peek_first_event_for_now() + { + return; + } + if self.main_module.did_initial_settle { + self.read_traces::(); + } else { + self.read_traces::(); + } + self.state.memory_write_log.sort_unstable(); + self.state.memory_write_log.dedup(); + self.main_module.did_initial_settle = true; + self.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( + this.trace_decls, + this.traces.0.len(), + this.trace_memories.len(), + )?)?, + ), + TraceWriterState::Init(trace_writer) => { + TraceWriterState::Running(this.init_trace_writer(trace_writer)?) + } + TraceWriterState::Running(trace_writer) => { + TraceWriterState::Running(this.update_trace_writer(trace_writer)?) + } + TraceWriterState::Errored(e) => TraceWriterState::Errored(e), + }) + }); + self.state.memory_write_log.clear(); + } + fn write_traces_change_time_to(&mut self, new_instant: SimInstant) { + self.for_each_trace_writer_storing_error(|_this, mut trace_writer_state| { + match &mut trace_writer_state { + TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(), + TraceWriterState::Running(trace_writer) => { + trace_writer.change_time_to(new_instant)?; + } + TraceWriterState::Errored(_) => {} + } + Ok(trace_writer_state) + }); + } + #[track_caller] + fn run_until( this_ref: &Rc>, - generator_waker: &std::task::Waker, - extern_modules_ready_to_run: &[usize], + run_target: &mut dyn FnMut(SimInstant) -> Option, ) { 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!( this.main_module.uninitialized_ios.is_empty(), "didn't initialize all inputs", ); - let run_target = run_target.max(this.instant); + this.check_waiting_sensitivity_sets(); + let arc_event_queue = this.event_queue.clone(); // avoid borrow errors + let mut event_queue = arc_event_queue.lock(); + let Some(run_target) = run_target(event_queue.instant()) else { + drop(event_queue); + panic!("SimInstant overflowed"); + }; + let run_target = run_target.max(event_queue.instant()); let mut settle_cycle = 0; - let mut run_extern_modules = true; loop { - assert!(settle_cycle < 100000, "settle(): took too many steps"); + if settle_cycle >= 100000 { + drop(event_queue); + panic!("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, + let event_queue_instant = event_queue.instant(); + let mut changed_time = false; + let first_entry = EventQueueData::first_event(event_queue, arc_event_queue.clone()) + .map_err(|mut event_queue| { + changed_time = event_queue_instant != run_target; + event_queue.set_instant(run_target); + drop(event_queue); + }); + let Ok(first_entry) = first_entry else { + if changed_time { + this.write_traces_change_time_to(run_target); + } + return; }; - if next_wait_target > this.instant { - settle_cycle = 0; - this.set_instant_no_sim(next_wait_target); - } - 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; + let Event { + instant: event_instant, + kind: event_kind, + } = *first_entry.event(); + if event_instant <= event_queue_instant { + first_entry.remove_and_wake_all_wakers(); + match event_kind { + EventKind::State => this.run_state_settle_cycle(), + EventKind::ExternModule(module_index) => { + drop(this); + Self::run_extern_module(this_ref, module_index); + this = this_ref.borrow_mut(); + } + } + this.write_traces_after_event(); + this.check_waiting_sensitivity_sets(); + } else { + event_queue = first_entry.into_event_queue_lock(); + let new_instant = event_instant.min(run_target); + let changed_time = event_queue_instant != new_instant; + event_queue.set_instant(new_instant); + drop(event_queue); + if changed_time { + this.write_traces_change_time_to(new_instant); + } + if event_instant > run_target { + return; } } - if this.main_module.did_initial_settle { - this.read_traces::(); - } else { - this.read_traces::(); - } - 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( - this.trace_decls, - this.traces.0.len(), - this.trace_memories.len(), - )?)?, - ), - TraceWriterState::Init(trace_writer) => { - TraceWriterState::Running(this.init_trace_writer(trace_writer)?) - } - TraceWriterState::Running(trace_writer) => { - TraceWriterState::Running(this.update_trace_writer(trace_writer)?) - } - TraceWriterState::Errored(e) => TraceWriterState::Errored(e), - }) - }); - this.state.memory_write_log.clear(); - } - if run_target > this.instant { - this.set_instant_no_sim(run_target); + event_queue = arc_event_queue.lock(); } } #[track_caller] fn settle(this_ref: &Rc>) { - let run_target = this_ref.borrow().instant; - Self::run_until(this_ref, run_target); + Self::run_until(this_ref, &mut Some); } fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState { match which_module { @@ -2092,10 +2695,11 @@ impl SimulationImpl { fn read_bit( &mut self, io: Expr, + read_time: ReadTime, which_module: WhichModule, ) -> MaybeNeedsSettle { self.get_module(which_module) - .read_helper(Expr::canonical(io), which_module) + .read_helper(Expr::canonical(io), read_time, which_module) .map(|compiled_value| ReadBitFn { compiled_value }) .apply_no_settle(&mut self.state) } @@ -2104,7 +2708,7 @@ impl SimulationImpl { let compiled_value = self .get_module_mut(which_module) .write_helper(io, which_module); - self.state_ready_to_run = true; + self.event_queue.add_event_for_now(EventKind::State); match compiled_value.range.len().as_single() { Some(TypeLenSingle::SmallSlot) => { self.state.small_slots[compiled_value.range.small_slots.start] = value as _; @@ -2114,15 +2718,17 @@ impl SimulationImpl { } Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), } + self.debug_write(compiled_value); } #[track_caller] fn read_bool_or_int( &mut self, io: Expr, + read_time: ReadTime, which_module: WhichModule, ) -> MaybeNeedsSettle, I::Value> { self.get_module(which_module) - .read_helper(Expr::canonical(io), which_module) + .read_helper(Expr::canonical(io), read_time, which_module) .map(|compiled_value| ReadBoolOrIntFn { compiled_value, io }) .apply_no_settle(&mut self.state) } @@ -2136,7 +2742,7 @@ impl SimulationImpl { let compiled_value = self .get_module_mut(which_module) .write_helper(Expr::canonical(io), which_module); - self.state_ready_to_run = true; + self.event_queue.add_event_for_now(EventKind::State); let value: BigInt = value.into(); match compiled_value.range.len().as_single() { Some(TypeLenSingle::SmallSlot) => { @@ -2151,6 +2757,7 @@ impl SimulationImpl { } Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), } + self.debug_write(compiled_value); } #[track_caller] fn read_write_sim_value_helper( @@ -2203,17 +2810,18 @@ impl SimulationImpl { None => unreachable!(), } } - CompiledTypeLayoutBody::Array { element } => { + CompiledTypeLayoutBody::Array { elements_non_empty } => { let ty = ::from_canonical(compiled_value.layout.ty); let element_size = ty.element().size(); for element_index in 0..ty.len() { Self::read_write_sim_value_helper( state, CompiledValue { - layout: *element, - range: compiled_value - .range - .index_array(element.layout.len(), element_index), + layout: elements_non_empty[element_index], + range: compiled_value.range.index_array( + elements_non_empty[element_index].layout.len(), + element_index, + ), write: None, }, start_index + element_index * element_size, @@ -2224,6 +2832,7 @@ impl SimulationImpl { ); } } + CompiledTypeLayoutBody::PhantomConst => {} CompiledTypeLayoutBody::Bundle { fields } => { let ty = Bundle::from_canonical(compiled_value.layout.ty); for ( @@ -2280,7 +2889,7 @@ impl SimulationImpl { }, ); } - let size = Expr::ty(io).size(); + let size = io.ty().size(); let mut opaque = OpaqueSimValue::with_capacity(size); opaque.rewrite_with(size, |mut writer| { SimulationImpl::read_write_sim_value_helper( @@ -2313,7 +2922,7 @@ impl SimulationImpl { ); writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) }); - SimValue::from_opaque(Expr::ty(io), opaque) + SimValue::from_opaque(io.ty(), opaque) } /// doesn't modify `opaque` fn value_changed( @@ -2350,15 +2959,23 @@ impl SimulationImpl { any_change.get() } #[track_caller] + fn is_reset_async(&self, io: Expr, which_module: WhichModule) -> bool { + self.get_module(which_module) + .is_reset_async(io, which_module) + } + #[track_caller] fn read( &mut self, io: Expr, + read_time: ReadTime, which_module: WhichModule, ) -> ( CompiledValue, MaybeNeedsSettle>, ) { - let compiled_value = self.get_module(which_module).read_helper(io, which_module); + let compiled_value = self + .get_module(which_module) + .read_helper(io, read_time, which_module); let value = compiled_value .map(|compiled_value| ReadFn { compiled_value, io }) .apply_no_settle(&mut self.state); @@ -2366,6 +2983,18 @@ impl SimulationImpl { | MaybeNeedsSettle::NoSettleNeeded(compiled_value)) = compiled_value; (compiled_value, value) } + fn debug_write(&self, compiled_value: CompiledValue) { + if self.breakpoints.as_ref().is_some_and(|v| v.trace) { + println!( + "wrote: {:#?}", + compiler::DebugCompiledValueStateAsMap { + compiled_value, + state_layout: self.state.insns.state_layout(), + state: &self.state, + }, + ); + } + } #[track_caller] fn write( &mut self, @@ -2376,8 +3005,8 @@ impl SimulationImpl { 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.event_queue.add_event_for_now(EventKind::State); + assert_eq!(io.ty(), value.ty()); Self::read_write_sim_value_helper( &mut self.state, compiled_value, @@ -2409,6 +3038,7 @@ impl SimulationImpl { value.clone_from(&opaque.sim_only_values()[index]); }, ); + self.debug_write(compiled_value); } #[track_caller] fn settle_if_needed(this_ref: &Rc>, v: MaybeNeedsSettle) -> O @@ -2424,7 +3054,7 @@ impl SimulationImpl { } } async fn yield_settle_if_needed( - this_ref: &Rc>, + this_ref: &RefCell, module_index: usize, v: MaybeNeedsSettle, ) -> O @@ -2433,7 +3063,7 @@ impl SimulationImpl { { match v { MaybeNeedsSettle::NeedsSettle(v) => { - Self::yield_advance_time_or_settle(this_ref.clone(), module_index, None).await; + Self::yield_advance_time_or_settle(this_ref, module_index, None).await; v.call(&mut this_ref.borrow_mut().state) } MaybeNeedsSettle::NoSettleNeeded(v) => v, @@ -2574,11 +3204,14 @@ impl fmt::Debug for SortedSetDebug<'_, T, V> { } } -struct SortedMapDebug<'a, K: 'static + Send + Sync, V>(&'a BTreeMap, V>); +struct SortedMapDebug<'a, T>(&'a T); -impl fmt::Debug for SortedMapDebug<'_, K, V> { +impl<'a, K: fmt::Debug + 'a, V: fmt::Debug + 'a, T> fmt::Debug for SortedMapDebug<'a, T> +where + &'a T: IntoIterator, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut entries = Vec::from_iter(self.0.iter().map(|(k, v)| { + let mut entries = Vec::from_iter(self.0.into_iter().map(|(k, v)| { if f.alternate() { (format!("{k:#?}"), format!("{v:#?}")) } else { @@ -2629,23 +3262,19 @@ macro_rules! impl_simulation_methods { let retval = $self .sim_impl .borrow_mut() - .read_bool_or_int(io, $which_module); + .read_bool_or_int(io, ReadTime::Current, $which_module); $self.settle_if_needed(retval)$(.$await)? } $(#[$track_caller])? pub $($async)? fn write_bool_or_int( &mut $self, io: Expr, - value: impl ToExpr, + value: impl ToSimValueWithType, ) { - 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"); + let value = value.to_sim_value_with_type(io.ty()); $self.sim_impl.borrow_mut().write_bool_or_int( io, - I::bits_to_value(Cow::Borrowed(&value)), + SimValue::into_value(value), $which_module, ); } @@ -2660,7 +3289,7 @@ macro_rules! impl_simulation_methods { let retval = $self .sim_impl .borrow_mut() - .read_bit(Expr::canonical(io), $which_module); + .read_bit(Expr::canonical(io), ReadTime::Current, $which_module); $self.settle_if_needed(retval)$(.$await)? } $(#[$track_caller])? @@ -2674,7 +3303,7 @@ macro_rules! impl_simulation_methods { let retval = $self .sim_impl .borrow_mut() - .read_bit(Expr::canonical(io), $which_module); + .read_bit(Expr::canonical(io), ReadTime::Current, $which_module); $self.settle_if_needed(retval)$(.$await)? } $(#[$track_caller])? @@ -2688,22 +3317,29 @@ macro_rules! impl_simulation_methods { let retval = $self .sim_impl .borrow_mut() - .read_bit(Expr::canonical(io), $which_module); + .read_bit(Expr::canonical(io), ReadTime::Current, $which_module); $self.settle_if_needed(retval)$(.$await)? } + #[track_caller] + pub fn is_reset_async(&$self, io: Expr) -> bool { + $self + .sim_impl + .borrow_mut() + .is_reset_async(Expr::canonical(io), $which_module) + } $(#[$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; + .read(Expr::canonical(io), ReadTime::Current, $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))), + &SimValue::into_canonical(value.into_sim_value_with_type(io.ty())), $which_module, ); } @@ -2773,18 +3409,19 @@ impl Simulation { #[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.borrow_mut().breakpoints = Some(BreakpointsSet { + let mut sim_impl = self.sim_impl.borrow_mut(); + sim_impl.breakpoints = Some(BreakpointsSet { last_was_break: false, set: pcs, trace, }); + sim_impl.event_queue.lock().trace = trace; } } pub struct ExternModuleSimulationState { sim_impl: Rc>, module_index: usize, - wait_for_changes_wait_targets: EarliestWaitTargets, } impl fmt::Debug for ExternModuleSimulationState { @@ -2792,7 +3429,6 @@ impl fmt::Debug for ExternModuleSimulationState { let Self { sim_impl: _, module_index, - wait_for_changes_wait_targets: _, } = self; f.debug_struct("ExternModuleSimulationState") .field("sim_impl", &DebugAsDisplay("...")) @@ -2803,12 +3439,11 @@ impl fmt::Debug for ExternModuleSimulationState { impl ExternModuleSimulationState { pub async fn settle(&mut self) { - SimulationImpl::yield_advance_time_or_settle(self.sim_impl.clone(), self.module_index, None) - .await + SimulationImpl::yield_advance_time_or_settle(&self.sim_impl, 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.sim_impl, self.module_index, Some(duration), ) @@ -2819,27 +3454,39 @@ impl ExternModuleSimulationState { iter: I, timeout: Option, ) { - self.wait_for_changes_wait_targets.clear(); + let mut sim_impl = self.sim_impl.borrow_mut(); + let mut sensitivity_set = SensitivitySet { + debug_id: sim_impl.next_sensitivity_set_debug_id, + compiled_values: HashSet::default(), + values: Vec::new(), + waker: RefCell::new(std::task::Waker::noop().clone()), + changed: Cell::new(false), + }; + sim_impl.next_sensitivity_set_debug_id = + sim_impl.next_sensitivity_set_debug_id.wrapping_add(1); + drop(sim_impl); 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 (key, value) = self + .sim_impl + .borrow_mut() + .read(io, ReadTime::Current, which_module); let value = self.settle_if_needed(value).await; - self.wait_for_changes_wait_targets - .insert(WaitTarget::Change { key, value }); + let key = Rc::new(key); + if sensitivity_set.compiled_values.insert(key.clone()) { + sensitivity_set.values.push((key, Rc::new(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(), + SimulationImpl::yield_wait_for_changes( + &self.sim_impl, self.module_index, - &mut self.wait_for_changes_wait_targets, + sensitivity_set, + timeout, ) - .await; + .await } pub async fn wait_for_clock_edge(&mut self, clk: impl ToExpr) { let clk = clk.to_expr(); @@ -2856,6 +3503,330 @@ impl ExternModuleSimulationState { { SimulationImpl::yield_settle_if_needed(&self.sim_impl, self.module_index, v).await } + pub async fn fork_join(&mut self, futures: F) -> F::Output { + F::fork_join(futures, self).await + } + async fn resettable_helper( + &mut self, + cancellable: &Cell, + wait_for_reset: impl AsyncFn(Self), + main: impl AsyncFn(Self), + ) -> ! { + let mut wait_for_reset_invocation = pin!(Some(wait_for_reset(self.forked_state()))); + let mut main_invocation = pin!(Some(main(self.forked_state()))); + std::future::poll_fn(|cx: &mut std::task::Context<'_>| { + loop { + if cancellable.get() && wait_for_reset_invocation.is_none() { + cancellable.set(false); + main_invocation.set(Some(main(self.forked_state()))); + wait_for_reset_invocation.set(Some(wait_for_reset(self.forked_state()))) + } + match main_invocation.as_mut().as_pin_mut().map(|f| f.poll(cx)) { + None | Some(Poll::Pending) => {} + Some(Poll::Ready(())) => { + main_invocation.set(None); + continue; + } + } + match wait_for_reset_invocation + .as_mut() + .as_pin_mut() + .map(|f| f.poll(cx)) + { + None | Some(Poll::Pending) => {} + Some(Poll::Ready(())) => { + wait_for_reset_invocation.set(None); + continue; + } + } + return Poll::Pending; + } + }) + .await + } + /// When `cd.rst` is deduced to be an [`AsyncReset`]: + /// * when `cd.rst` is asserted or when first called, `reset` is invoked and any running `run` invocation is cancelled. + /// * when `cd.rst` is de-asserted and when `reset` finishes, `run` is invoked. + /// + /// When `cd.rst` is deduced to be a [`SyncReset`]: + /// * when there's a positive-going clock edge on `cd.clk` and `cd.rst` is asserted or when first called, `reset` is invoked and any running `run` invocation is cancelled. + /// * when `reset` finishes, `run` is invoked. + pub async fn resettable( + &mut self, + cd: impl ToExpr>, + reset: impl AsyncFn(Self) -> T, + run: impl AsyncFn(Self, T), + ) -> ! { + let cd = cd.to_expr(); + let rst = cd.rst; + if self.is_reset_async(rst) { + let cancellable = Cell::new(false); + let wait_for_reset = |mut this: Self| async move { + while this.read_reset(rst).await { + this.wait_for_changes([rst], None).await; + } + while !this.read_reset(rst).await { + this.wait_for_changes([rst], None).await; + } + }; + let main = |mut this: Self| async { + let run_arg = reset(this.forked_state()).await; + cancellable.set(true); + while this.read_reset(rst).await { + this.wait_for_changes([rst], None).await; + } + run(this, run_arg).await + }; + self.resettable_helper(&cancellable, wait_for_reset, main) + .await + } else { + let clk = cd.clk; + let wait_for_reset = |mut this: Self| async move { + loop { + this.wait_for_clock_edge(clk).await; + if this.read_reset(rst).await { + return; + } + } + }; + let cancellable = Cell::new(false); + let main = |this: Self| async { + let run_arg = reset(this.forked_state()).await; + cancellable.set(true); + run(this, run_arg).await + }; + self.resettable_helper(&cancellable, wait_for_reset, main) + .await + } + } + fn forked_state(&self) -> Self { + let Self { + ref sim_impl, + module_index, + } = *self; + Self { + sim_impl: sim_impl.clone(), + module_index, + } + } + pub async fn fork_join_scope<'env, F, Fut>( + &mut self, + in_scope: F, + ) -> ::Output + where + F: FnOnce(ForkJoinScope<'env>, ExternModuleSimulationState) -> Fut, + Fut: IntoFuture>, + { + let scope = ForkJoinScope { + new_tasks: Rc::new(RefCell::new(vec![])), + sim: self.forked_state(), + }; + let join_handle = scope.spawn(in_scope); + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] + struct TaskId(u64); + struct TasksStateInner { + next_task_id: u64, + ready_tasks: BTreeSet, + not_ready_tasks: BTreeSet, + base_waker: std::task::Waker, + } + impl Default for TasksStateInner { + fn default() -> Self { + Self { + next_task_id: Default::default(), + ready_tasks: Default::default(), + not_ready_tasks: Default::default(), + base_waker: std::task::Waker::noop().clone(), + } + } + } + #[derive(Default)] + struct TasksState { + inner: Mutex, + } + #[derive(Clone)] + struct TaskWaker { + state: std::sync::Weak, + task: TaskId, + } + impl std::task::Wake for TaskWaker { + fn wake(self: Arc) { + self.wake_by_ref(); + } + fn wake_by_ref(self: &Arc) { + let Some(state) = self.state.upgrade() else { + return; + }; + let mut inner = state.inner.lock().expect("not poisoned"); + if inner.not_ready_tasks.remove(&self.task) { + inner.ready_tasks.insert(self.task); + inner.base_waker.wake_by_ref(); + } + } + } + struct Task<'env> { + task: Pin + 'env>>, + waker: std::task::Waker, + } + let mut tasks: BTreeMap = BTreeMap::new(); + let tasks_state = Arc::new(TasksState::default()); + std::future::poll_fn(move |cx: &mut std::task::Context<'_>| { + let mut state_inner = tasks_state.inner.lock().expect("not poisoned"); + state_inner.base_waker.clone_from(cx.waker()); + loop { + for new_task in scope.new_tasks.borrow_mut().drain(..) { + let task_id = TaskId(state_inner.next_task_id); + let Some(next_task_id) = state_inner.next_task_id.checked_add(1) else { + drop(state_inner); + panic!("spawned too many tasks"); + }; + state_inner.next_task_id = next_task_id; + state_inner.ready_tasks.insert(task_id); + tasks.insert( + task_id, + Task { + task: new_task, + waker: Arc::new(TaskWaker { + state: Arc::downgrade(&tasks_state), + task: task_id, + }) + .into(), + }, + ); + } + let Some(task_id) = state_inner.ready_tasks.pop_first() else { + if state_inner.not_ready_tasks.is_empty() { + return Poll::Ready(()); + } else { + return Poll::Pending; + }; + }; + state_inner.not_ready_tasks.insert(task_id); // task can be woken while we're running poll + drop(state_inner); + let std::collections::btree_map::Entry::Occupied(mut entry) = tasks.entry(task_id) + else { + unreachable!(); + }; + let Task { task, waker } = entry.get_mut(); + match task.as_mut().poll(&mut std::task::Context::from_waker( + &std::task::Waker::from(waker.clone()), + )) { + Poll::Pending => { + state_inner = tasks_state.inner.lock().expect("not poisoned"); + continue; + } + Poll::Ready(()) => {} + } + drop(entry.remove()); // drop outside lock + state_inner = tasks_state.inner.lock().expect("not poisoned"); + state_inner.not_ready_tasks.remove(&task_id); + state_inner.ready_tasks.remove(&task_id); + } + }) + .await; + match &mut *join_handle.state.borrow_mut() { + JoinHandleState::Running(_) => unreachable!(), + JoinHandleState::Finished(state) => state + .take() + .expect("filled by running all futures to completion"), + } + } + /// Reads the value of `io` from right before the last clock edge of `clock_for_past`. + /// + /// In order to use the `read_past()` family of functions, you must call [`m.register_clock_for_past(my_io.clk)`] + /// in the module's body for every clock you use as the second argument of the `read_past()` family. + /// + /// [`m.register_clock_for_past(my_io.clk)`]: crate::module::ModuleBuilder::register_clock_for_past + pub async fn read_past_bool_or_int( + &mut self, + io: Expr, + clock_for_past: Expr, + ) -> I::Value { + let retval = self.sim_impl.borrow_mut().read_bool_or_int( + io, + ReadTime::Past { clock_for_past }, + WhichModule::Extern { + module_index: self.module_index, + }, + ); + self.settle_if_needed(retval).await + } + /// Reads the value of `io` from right before the last clock edge of `clock_for_past`. + /// + /// In order to use the `read_past()` family of functions, you must call [`m.register_clock_for_past(my_io.clk)`] + /// in the module's body for every clock you use as the second argument of the `read_past()` family. + /// + /// [`m.register_clock_for_past(my_io.clk)`]: crate::module::ModuleBuilder::register_clock_for_past + pub async fn read_past_clock(&mut self, io: Expr, clock_for_past: Expr) -> bool { + let retval = self.sim_impl.borrow_mut().read_bit( + Expr::canonical(io), + ReadTime::Past { clock_for_past }, + WhichModule::Extern { + module_index: self.module_index, + }, + ); + self.settle_if_needed(retval).await + } + /// Reads the value of `io` from right before the last clock edge of `clock_for_past`. + /// + /// In order to use the `read_past()` family of functions, you must call [`m.register_clock_for_past(my_io.clk)`] + /// in the module's body for every clock you use as the second argument of the `read_past()` family. + /// + /// [`m.register_clock_for_past(my_io.clk)`]: crate::module::ModuleBuilder::register_clock_for_past + pub async fn read_past_bool(&mut self, io: Expr, clock_for_past: Expr) -> bool { + let retval = self.sim_impl.borrow_mut().read_bit( + Expr::canonical(io), + ReadTime::Past { clock_for_past }, + WhichModule::Extern { + module_index: self.module_index, + }, + ); + self.settle_if_needed(retval).await + } + /// Reads the value of `io` from right before the last clock edge of `clock_for_past`. + /// + /// In order to use the `read_past()` family of functions, you must call [`m.register_clock_for_past(my_io.clk)`] + /// in the module's body for every clock you use as the second argument of the `read_past()` family. + /// + /// [`m.register_clock_for_past(my_io.clk)`]: crate::module::ModuleBuilder::register_clock_for_past + pub async fn read_past_reset( + &mut self, + io: Expr, + clock_for_past: Expr, + ) -> bool { + let retval = self.sim_impl.borrow_mut().read_bit( + Expr::canonical(io), + ReadTime::Past { clock_for_past }, + WhichModule::Extern { + module_index: self.module_index, + }, + ); + self.settle_if_needed(retval).await + } + /// Reads the value of `io` from right before the last clock edge of `clock_for_past`. + /// + /// In order to use the `read_past()` family of functions, you must call [`m.register_clock_for_past(my_io.clk)`] + /// in the module's body for every clock you use as the second argument of the `read_past()` family. + /// + /// [`m.register_clock_for_past(my_io.clk)`]: crate::module::ModuleBuilder::register_clock_for_past + pub async fn read_past( + &mut self, + io: Expr, + clock_for_past: Expr, + ) -> SimValue { + let retval = self + .sim_impl + .borrow_mut() + .read( + Expr::canonical(io), + ReadTime::Past { clock_for_past }, + WhichModule::Extern { + module_index: self.module_index, + }, + ) + .1; + SimValue::from_canonical(self.settle_if_needed(retval).await) + } impl_simulation_methods!( async_await = (async, await), track_caller = (), @@ -2863,6 +3834,233 @@ impl ExternModuleSimulationState { ); } +pub struct ForkJoinScope<'env> { + new_tasks: Rc + 'env>>>>>, + sim: ExternModuleSimulationState, +} + +impl<'env> Clone for ForkJoinScope<'env> { + fn clone(&self) -> Self { + Self { + new_tasks: self.new_tasks.clone(), + sim: self.sim.forked_state(), + } + } +} + +impl<'env> ForkJoinScope<'env> { + fn spawn_inner(&self, fut: Pin + 'env>>) { + self.new_tasks.borrow_mut().push(fut); + } + pub fn spawn_detached_future( + &self, + fut: impl IntoFuture>, + ) { + self.spawn_inner(Box::pin(fut.into_future())); + } + pub fn spawn_detached(&self, f: F) + where + F: FnOnce(ForkJoinScope<'env>, ExternModuleSimulationState) -> Fut, + Fut: IntoFuture>, + { + self.spawn_detached_future(f(self.clone(), self.sim.forked_state())); + } + pub fn spawn(&self, f: F) -> JoinHandle + where + F: FnOnce(ForkJoinScope<'env>, ExternModuleSimulationState) -> Fut, + Fut: IntoFuture>, + { + let join_handle = JoinHandle { + state: Default::default(), + }; + let state = Rc::downgrade(&join_handle.state); + let fut = f(self.clone(), self.sim.forked_state()).into_future(); + self.spawn_detached_future(async move { + let result = fut.await; + let Some(state) = state.upgrade() else { return }; + let mut state = state.borrow_mut(); + let waker = match &mut *state { + JoinHandleState::Running(waker) => waker.take(), + JoinHandleState::Finished(_) => unreachable!(), + }; + *state = JoinHandleState::Finished(Some(result)); + drop(state); + let Some(waker) = waker else { return }; + waker.wake(); + }); + join_handle + } +} + +enum JoinHandleState { + Running(Option), + Finished(Option), +} + +impl Default for JoinHandleState { + fn default() -> Self { + Self::Running(None) + } +} + +pub struct JoinHandle { + state: Rc>>, +} + +impl JoinHandle { + pub fn is_finished(&self) -> bool { + matches!(*self.state.borrow(), JoinHandleState::Finished(_)) + } + pub fn try_join(self) -> Result { + let mut state = self.state.borrow_mut(); + match &mut *state { + JoinHandleState::Running(_) => { + drop(state); + Err(self) + } + JoinHandleState::Finished(retval) => { + let Some(retval) = retval.take() else { + panic!("already returned the value in poll"); + }; + Ok(retval) + } + } + } + pub async fn join(self) -> T { + self.await + } +} + +impl Future for JoinHandle { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + match &mut *self.state.borrow_mut() { + JoinHandleState::Running(waker) => { + match waker { + None => *waker = Some(cx.waker().clone()), + Some(waker) => waker.clone_from(cx.waker()), + } + Poll::Pending + } + JoinHandleState::Finished(retval) => { + let Some(retval) = retval.take() else { + panic!("already returned Poll::Ready"); + }; + Poll::Ready(retval) + } + } + } +} + +struct ForkJoinImpl<'a> { + futures: Vec + 'a>>>, +} + +impl Future for ForkJoinImpl<'_> { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + let Self { futures } = self.get_mut(); + futures.retain_mut(|future| future.as_mut().poll(cx).is_pending()); + if futures.is_empty() { + Poll::Ready(()) + } else { + Poll::Pending + } + } +} + +pub trait ForkJoin { + type Output; + #[allow(async_fn_in_trait, reason = "no auto traits needed")] + async fn fork_join(this: Self, sim_state: &mut ExternModuleSimulationState) -> Self::Output; +} + +impl O, O, const N: usize> ForkJoin for [F; N] { + type Output = [O; N]; + async fn fork_join(this: Self, sim_state: &mut ExternModuleSimulationState) -> Self::Output { + let mut temp = [const { None }; N]; + ForkJoinImpl { + futures: this + .into_iter() + .zip(&mut temp) + .map(|(future, temp)| -> Pin + '_>> { + Box::pin(async { *temp = Some(future(sim_state.forked_state()).await) }) + }) + .collect(), + } + .await; + temp.map(|output| output.expect("set to Some above")) + } +} + +impl O, O> ForkJoin for Vec { + type Output = Vec; + async fn fork_join(this: Self, sim_state: &mut ExternModuleSimulationState) -> Self::Output { + let mut temp = Vec::with_capacity(this.len()); + for _ in 0..this.len() { + temp.push(None); + } + ForkJoinImpl { + futures: this + .into_iter() + .zip(&mut temp) + .map(|(future, temp)| -> Pin + '_>> { + Box::pin(async { *temp = Some(future(sim_state.forked_state()).await) }) + }) + .collect(), + } + .await; + temp.into_iter() + .map(|output| output.expect("set to Some above")) + .collect() + } +} + +impl O, O> ForkJoin for Box<[F]> { + type Output = Box<[O]>; + async fn fork_join(this: Self, sim_state: &mut ExternModuleSimulationState) -> Self::Output { + Vec::fork_join(this.into(), sim_state) + .await + .into_boxed_slice() + } +} + +impl ForkJoin for Box { + type Output = Box; + async fn fork_join(this: Self, sim_state: &mut ExternModuleSimulationState) -> Self::Output { + Box::new(T::fork_join(*this, sim_state).await) + } +} + +macro_rules! impl_fork_join_tuples { + (@impl $(($f:ident: $F:ident => $o:ident: $O:ident)),*) => { + impl<$($F: AsyncFnOnce(ExternModuleSimulationState) -> $O, $O,)*> ForkJoin for ($($F,)*) { + type Output = ($($O,)*); + async fn fork_join(this: Self, sim_state: &mut ExternModuleSimulationState) -> Self::Output { + #![allow(unused_variables)] + let ($($f,)*) = this; + $(let mut $o = None;)* + ForkJoinImpl { + futures: vec![ + $(Box::pin(async { $o = Some($f(sim_state.forked_state()).await) }),)* + ], + }.await; + ($($o.expect("set to Some above"),)*) + } + } + }; + (($($first:tt)*) $(, $rest:tt)* $(,)?) => { + impl_fork_join_tuples!(@impl ($($first)*) $(, $rest)*); + impl_fork_join_tuples!($($rest),*); + }; + () => { + impl_fork_join_tuples!(@impl); + }; +} + +impl_fork_join_tuples!((f0: F0 => o0: O0), (f1: F1 => o1: O1), (f2: F2 => o2: O2), (f3: F3 => o3: O3), (f4: F4 => o4: O4), (f5: F5 => o5: O5), (f6: F6 => o6: O6), (f7: F7 => o7: O7), (f8: F8 => o8: O8), (f9: F9 => o9: O9), (f10: F10 => o10: O10), (f11: F11 => o11: O11),); + pub trait ExternModuleSimGenerator: Clone + Eq + Hash + Any + Send + Sync + fmt::Debug { fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture + 'a; } @@ -2963,7 +4161,7 @@ impl fmt::Debug for ExternModuleSimulation { .field("generator", &self.generator) .field( "sim_io_to_generator_map", - &SortedMapDebug(&self.sim_io_to_generator_map), + &SortedMapDebug(&*self.sim_io_to_generator_map), ) .field("source_location", &self.source_location) .finish() diff --git a/crates/fayalite/src/sim/compiler.rs b/crates/fayalite/src/sim/compiler.rs index 2b291bea..07621c5c 100644 --- a/crates/fayalite/src/sim/compiler.rs +++ b/crates/fayalite/src/sim/compiler.rs @@ -7,14 +7,14 @@ use crate::{ bundle::{BundleField, BundleType}, enum_::{EnumType, EnumVariant}, expr::{ - ExprEnum, Flow, ops, + ExprEnum, Flow, ValueType, ops, target::{ GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, }, int::BoolOrIntType, - intern::{Intern, Interned, Memoize}, + intern::{Intern, InternSlice, Interned, Memoize}, memory::PortKind, module::{ AnnotatedModuleIO, Block, ExternModuleBody, Id, InstantiatedModule, ModuleBody, NameId, @@ -28,12 +28,12 @@ use crate::{ 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, + TraceMemoryLocation, TraceModule, TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt, + TraceScalarId, TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire, interpreter::{ - Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding, - InsnsBuildingDone, InsnsBuildingKind, Label, SmallUInt, StatePartArrayIndex, - StatePartArrayIndexed, + self, Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding, + InsnsBuildingDone, InsnsBuildingKind, Label, PrefixLinesWrapper, SmallUInt, + StatePartArrayIndex, StatePartArrayIndexed, parts::{ MemoryData, SlotDebugData, StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, StatePartKindMemories, StatePartKindSimOnlySlots, @@ -82,19 +82,73 @@ pub(crate) struct CompiledBundleField { pub(crate) ty: CompiledTypeLayout, } +impl CompiledBundleField { + fn with_prefixed_debug_names(self, prefix: &str) -> Self { + let Self { offset, ty } = self; + Self { + offset, + ty: ty.with_prefixed_debug_names(prefix), + } + } + fn with_anonymized_debug_info(self) -> Self { + let Self { offset, ty } = self; + Self { + offset, + ty: ty.with_anonymized_debug_info(), + } + } +} + #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub(crate) enum CompiledTypeLayoutBody { Scalar, + PhantomConst, Array { - /// debug names are ignored, use parent's layout instead - element: Interned>, + /// always has at least one element even for zero-sized arrays + elements_non_empty: Interned<[CompiledTypeLayout]>, }, Bundle { - /// debug names are ignored, use parent's layout instead fields: Interned<[CompiledBundleField]>, }, } +impl CompiledTypeLayoutBody { + fn with_prefixed_debug_names(self, prefix: &str) -> Self { + match self { + CompiledTypeLayoutBody::Scalar | CompiledTypeLayoutBody::PhantomConst => self, + CompiledTypeLayoutBody::Array { elements_non_empty } => CompiledTypeLayoutBody::Array { + elements_non_empty: elements_non_empty + .iter() + .map(|element| element.with_prefixed_debug_names(prefix)) + .collect(), + }, + CompiledTypeLayoutBody::Bundle { fields } => CompiledTypeLayoutBody::Bundle { + fields: fields + .iter() + .map(|field| field.with_prefixed_debug_names(prefix)) + .collect(), + }, + } + } + fn with_anonymized_debug_info(self) -> Self { + match self { + CompiledTypeLayoutBody::Scalar | CompiledTypeLayoutBody::PhantomConst => self, + CompiledTypeLayoutBody::Array { elements_non_empty } => CompiledTypeLayoutBody::Array { + elements_non_empty: elements_non_empty + .iter() + .map(|element| element.with_anonymized_debug_info()) + .collect(), + }, + CompiledTypeLayoutBody::Bundle { fields } => CompiledTypeLayoutBody::Bundle { + fields: fields + .iter() + .map(|field| field.with_anonymized_debug_info()) + .collect(), + }, + } + } +} + #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub(crate) struct CompiledTypeLayout { pub(crate) ty: T, @@ -108,7 +162,7 @@ impl CompiledTypeLayout { Self { ty, layout: layout.with_prefixed_debug_names(prefix), - body, + body: body.with_prefixed_debug_names(prefix), } } fn with_anonymized_debug_info(self) -> Self { @@ -116,7 +170,7 @@ impl CompiledTypeLayout { Self { ty, layout: layout.with_anonymized_debug_info(), - body, + body: body.with_anonymized_debug_info(), } } fn get(ty: T) -> Self { @@ -151,28 +205,29 @@ impl CompiledTypeLayout { } CanonicalType::Array(array) => { let mut layout = TypeLayout::empty(); - let element = CompiledTypeLayout::get(array.element()).intern_sized(); + let element = CompiledTypeLayout::get(array.element()); + let mut elements_non_empty = vec![]; for index in 0..array.len() { - layout.allocate( - &element - .layout - .with_prefixed_debug_names(&format!("[{index}]")), - ); + let element = element.with_prefixed_debug_names(&format!("[{index}]")); + layout.allocate(&element.layout); + elements_non_empty.push(element); + } + if array.is_empty() { + elements_non_empty.push(element.with_prefixed_debug_names("[]")); } 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, + body: CompiledTypeLayoutBody::Array { + elements_non_empty: elements_non_empty.intern_deref(), + }, } } + CanonicalType::PhantomConst(_) => CompiledTypeLayout { + ty: *input, + layout: TypeLayout::empty(), + body: CompiledTypeLayoutBody::PhantomConst, + }, CanonicalType::Bundle(bundle) => { let mut layout = TypeLayout::empty(); let fields = bundle @@ -184,13 +239,9 @@ impl CompiledTypeLayout { flipped: _, ty, }| { - let ty = CompiledTypeLayout::get(*ty); - let offset = layout - .allocate( - &ty.layout - .with_prefixed_debug_names(&format!(".{name}")), - ) - .start(); + let ty = CompiledTypeLayout::get(*ty) + .with_prefixed_debug_names(&format!(".{name}")); + let offset = layout.allocate(&ty.layout).start(); CompiledBundleField { offset, ty } }, ) @@ -273,6 +324,39 @@ impl CompiledValue { } } +pub(crate) struct DebugCompiledValueStateAsMap<'a> { + pub(crate) compiled_value: CompiledValue, + pub(crate) state_layout: &'a interpreter::parts::StateLayout, + pub(crate) state: &'a interpreter::State, +} + +impl fmt::Debug for DebugCompiledValueStateAsMap<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use fmt::Write; + if self.compiled_value.range.is_empty() { + return f.write_str("{}"); + } + writeln!(f, "{{")?; + let mut f = PrefixLinesWrapper::new(f, true, " "); + macro_rules! debug_fmt { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + ) => { + $(for slot in self.compiled_value.range.$type_plural_field.iter() { + slot.debug_fmt(&mut f, ":", " ", " ", "", Some(self.state_layout), Some(self.state))?; + writeln!(f, ",")?; + })* + }; + } + get_state_part_kinds! { + debug_fmt! { + type_plural_fields; + } + } + write!(f.into_inner(), "}}") + } +} + impl CompiledValue { fn field_by_index(self, field_index: usize) -> CompiledValue { self.map(|layout, range| { @@ -301,10 +385,13 @@ impl CompiledValue { impl CompiledValue { pub(crate) fn element(self, index: usize) -> CompiledValue { self.map(|layout, range| { - let CompiledTypeLayoutBody::Array { element } = layout.body else { + let CompiledTypeLayoutBody::Array { elements_non_empty } = layout.body else { unreachable!(); }; - (*element, range.index_array(element.layout.len(), index)) + ( + elements_non_empty[index], + range.index_array(elements_non_empty[index].layout.len(), index), + ) }) } fn element_dyn( @@ -557,10 +644,11 @@ impl CompiledExpr { self, index_slot: StatePartIndex, ) -> CompiledExpr { - let CompiledTypeLayoutBody::Array { element } = self.static_part.layout.body else { + let CompiledTypeLayoutBody::Array { elements_non_empty } = self.static_part.layout.body + else { unreachable!(); }; - let stride = element.layout.len(); + let stride = elements_non_empty[0].layout.len(); let indexes = self.indexes.join(TypeArrayIndex::from_parts( index_slot, self.static_part.layout.ty.len(), @@ -568,10 +656,10 @@ impl CompiledExpr { )); CompiledExpr { static_part: self.static_part.map(|layout, range| { - let CompiledTypeLayoutBody::Array { element } = layout.body else { + let CompiledTypeLayoutBody::Array { elements_non_empty } = layout.body else { unreachable!(); }; - (*element, range.index_array(stride, 0)) + (elements_non_empty[0], range.index_array(stride, 0)) }), indexes, } @@ -1550,6 +1638,13 @@ struct ClockTrigger { source_location: SourceLocation, } +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub(crate) struct ExternModuleClockForPast { + pub(crate) clock_for_past: CompiledValue, + pub(crate) current_to_past_map: + Interned<[(CompiledValue, CompiledValue)]>, +} + #[derive(Debug)] struct Register { value: CompiledValue, @@ -1619,7 +1714,7 @@ impl MakeTraceDeclTarget { } fn ty(self) -> CanonicalType { match self { - MakeTraceDeclTarget::Expr(expr) => Expr::ty(expr), + MakeTraceDeclTarget::Expr(expr) => expr.ty(), MakeTraceDeclTarget::Memory { ty, .. } => ty, } } @@ -1637,7 +1732,9 @@ impl fmt::Debug for DebugOpaque { pub(crate) struct CompiledExternModule { pub(crate) module_io_targets: Interned<[Target]>, pub(crate) module_io: Interned<[CompiledValue]>, + pub(crate) clocks_for_past: Interned<[ExternModuleClockForPast]>, pub(crate) simulation: ExternModuleSimulation, + pub(crate) debug_name: Interned, } #[derive(Debug)] @@ -1681,18 +1778,23 @@ macro_rules! impl_compiler { instantiated_module: InstantiatedModule, target: MakeTraceDeclTarget, source_location: SourceLocation, + empty_kind: impl FnOnce() -> SimTraceKind, $($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!(), - })) + if compiled_value.range.is_empty() { + TraceLocation::Scalar(self.new_sim_trace(empty_kind())) + } else { + 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, @@ -1723,9 +1825,10 @@ macro_rules! impl_compiler { instantiated_module, target, source_location, + || unreachable!(), |index| SimTraceKind::SmallUInt { index, ty }, |index| SimTraceKind::BigUInt { index, ty }, - |_| unreachable!(""), + |_| unreachable!(), ), name, ty, @@ -1737,9 +1840,10 @@ macro_rules! impl_compiler { instantiated_module, target, source_location, + || unreachable!(), |index| SimTraceKind::SmallSInt { index, ty }, |index| SimTraceKind::BigSInt { index, ty }, - |_| unreachable!(""), + |_| unreachable!(), ), name, ty, @@ -1751,9 +1855,10 @@ macro_rules! impl_compiler { instantiated_module, target, source_location, + || unreachable!(), |index| SimTraceKind::SmallBool { index }, |index| SimTraceKind::BigBool { index }, - |_| unreachable!(""), + |_| unreachable!(), ), name, flow, @@ -1798,15 +1903,16 @@ macro_rules! impl_compiler { } .into() } - CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), CanonicalType::AsyncReset(_) => TraceAsyncReset { location: self.make_trace_scalar_helper( instantiated_module, target, source_location, + || unreachable!(), |index| SimTraceKind::SmallAsyncReset { index }, |index| SimTraceKind::BigAsyncReset { index }, - |_| unreachable!(""), + |_| unreachable!(), ), name, flow, @@ -1817,9 +1923,10 @@ macro_rules! impl_compiler { instantiated_module, target, source_location, + || unreachable!(), |index| SimTraceKind::SmallSyncReset { index }, |index| SimTraceKind::BigSyncReset { index }, - |_| unreachable!(""), + |_| unreachable!(), ), name, flow, @@ -1831,21 +1938,38 @@ macro_rules! impl_compiler { instantiated_module, target, source_location, + || unreachable!(), |index| SimTraceKind::SmallClock { index }, |index| SimTraceKind::BigClock { index }, - |_| unreachable!(""), + |_| unreachable!(), ), name, flow, } .into(), + CanonicalType::PhantomConst(ty) => TracePhantomConst { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + || SimTraceKind::PhantomConst { ty }, + |_| unreachable!(), + |_| unreachable!(), + |_| unreachable!(), + ), + name, + ty, + flow, + } + .into(), CanonicalType::DynSimOnly(ty) => TraceSimOnly { location: self.make_trace_scalar_helper( instantiated_module, target, source_location, - |_| unreachable!(""), - |_| unreachable!(""), + || unreachable!(), + |_| unreachable!(), + |_| unreachable!(), |index| SimTraceKind::SimOnly { index, ty }, ), name, @@ -2295,16 +2419,10 @@ impl Compiler { | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) | CanonicalType::Clock(_) - | CanonicalType::DynSimOnly(_) => { + | CanonicalType::DynSimOnly(_) + | CanonicalType::PhantomConst(_) => { 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( @@ -2653,7 +2771,7 @@ impl Compiler { let retval = parts .into_iter() .map(|part| part.cast_to_bits()) - .reduce(|accumulator, part| accumulator | (part << Expr::ty(accumulator).width)) + .reduce(|accumulator, part| accumulator | (part << accumulator.ty().width)) .unwrap_or_else(|| UInt[0].zero().to_expr()); let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); let retval = self @@ -2665,7 +2783,7 @@ impl Compiler { instantiated_module: InstantiatedModule, expr: ops::CastToBits, ) -> CompiledValue { - match Expr::ty(expr.arg()) { + match expr.arg().ty() { CanonicalType::UInt(_) => { self.compile_cast_scalar_to_bits(instantiated_module, expr.arg(), |arg| arg) } @@ -2831,7 +2949,7 @@ impl Compiler { return retval; } let mut cast_bit = |arg: Expr| { - let src_signed = match Expr::ty(arg) { + let src_signed = match arg.ty() { CanonicalType::UInt(_) => false, CanonicalType::SInt(_) => true, CanonicalType::Bool(_) => false, @@ -2845,7 +2963,7 @@ impl Compiler { CanonicalType::PhantomConst(_) => unreachable!(), CanonicalType::DynSimOnly(_) => unreachable!(), }; - let dest_signed = match Expr::ty(expr) { + let dest_signed = match expr.ty() { CanonicalType::UInt(_) => false, CanonicalType::SInt(_) => true, CanonicalType::Bool(_) => false, @@ -2859,22 +2977,23 @@ impl Compiler { 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, - }], + self.simple_nary_big_expr(instantiated_module, expr.ty(), [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() }; @@ -2914,21 +3033,13 @@ impl Compiler { }) .into(), ExprEnum::PhantomConst(_) => self - .compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default()) + .compile_aggregate_literal(instantiated_module, expr.ty(), Interned::default()) .into(), ExprEnum::BundleLiteral(literal) => self - .compile_aggregate_literal( - instantiated_module, - Expr::ty(expr), - literal.field_values(), - ) + .compile_aggregate_literal(instantiated_module, expr.ty(), literal.field_values()) .into(), ExprEnum::ArrayLiteral(literal) => self - .compile_aggregate_literal( - instantiated_module, - Expr::ty(expr), - literal.element_values(), - ) + .compile_aggregate_literal(instantiated_module, expr.ty(), literal.element_values()) .into(), ExprEnum::EnumLiteral(expr) => { let enum_bits_ty = UInt[expr.ty().type_properties().bit_width]; @@ -2956,13 +3067,13 @@ impl Compiler { ExprEnum::NotU(expr) => self .simple_nary_big_expr( instantiated_module, - Expr::ty(expr.arg()).canonical(), + expr.arg().ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { vec![Insn::NotU { dest, src, - width: Expr::ty(expr.arg()).width(), + width: expr.arg().ty().width(), }] }, ) @@ -2970,7 +3081,7 @@ impl Compiler { ExprEnum::NotS(expr) => self .simple_nary_big_expr( instantiated_module, - Expr::ty(expr.arg()).canonical(), + expr.arg().ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| vec![Insn::NotS { dest, src }], ) @@ -2978,7 +3089,7 @@ impl Compiler { ExprEnum::NotB(expr) => self .simple_nary_big_expr( instantiated_module, - Expr::ty(expr.arg()).canonical(), + expr.arg().ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { vec![Insn::NotU { @@ -3482,12 +3593,12 @@ impl Compiler { .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(); + let start = variant_access.base().ty().discriminant_bit_width(); + let len = expr.ty().bit_width(); self.compile_expr( instantiated_module, variant_access.base().cast_to_bits()[start..start + len] - .cast_bits_to(Expr::ty(expr)), + .cast_bits_to(expr.ty()), ) } ExprEnum::ArrayIndex(expr) => self @@ -3509,45 +3620,39 @@ impl Compiler { .map_ty(Array::from_canonical) .element_dyn(index_slot) } - ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 { + ExprEnum::ReduceBitAndU(expr) => if expr.arg().ty().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)), - ), + Expr::canonical(expr.arg().cmp_eq(expr.arg().ty().from_int_wrapping(-1))), ) } .into(), - ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 { + ExprEnum::ReduceBitAndS(expr) => if expr.arg().ty().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)), - ), + Expr::canonical(expr.arg().cmp_eq(expr.arg().ty().from_int_wrapping(-1))), ) } .into(), - ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 { + ExprEnum::ReduceBitOrU(expr) => if expr.arg().ty().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))), + Expr::canonical(expr.arg().cmp_ne(expr.arg().ty().from_int_wrapping(0))), ) } .into(), - ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 { + ExprEnum::ReduceBitOrS(expr) => if expr.arg().ty().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))), + Expr::canonical(expr.arg().cmp_ne(expr.arg().ty().from_int_wrapping(0))), ) } .into(), @@ -3560,7 +3665,7 @@ impl Compiler { vec![Insn::ReduceBitXor { dest, src, - input_width: Expr::ty(expr.arg()).width(), + input_width: expr.arg().ty().width(), }] }, ) @@ -3574,7 +3679,7 @@ impl Compiler { vec![Insn::ReduceBitXor { dest, src, - input_width: Expr::ty(expr.arg()).width(), + input_width: expr.arg().ty().width(), }] }, ) @@ -3672,8 +3777,8 @@ impl Compiler { mut rhs: Expr, source_location: SourceLocation, ) { - if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() { - match Expr::ty(lhs) { + if lhs.ty() != rhs.ty() || !lhs.ty().is_passive() { + match lhs.ty() { CanonicalType::UInt(lhs_ty) => { rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); } @@ -3682,7 +3787,7 @@ impl Compiler { } CanonicalType::Bool(_) => unreachable!(), CanonicalType::Array(lhs_ty) => { - let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else { + let CanonicalType::Array(rhs_ty) = rhs.ty() else { unreachable!(); }; assert_eq!(lhs_ty.len(), rhs_ty.len()); @@ -3702,13 +3807,13 @@ impl Compiler { return; } CanonicalType::Enum(lhs_ty) => { - let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else { + let CanonicalType::Enum(rhs_ty) = rhs.ty() else { unreachable!(); }; todo!("handle connect with different enum types"); } CanonicalType::Bundle(lhs_ty) => { - let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else { + let CanonicalType::Bundle(rhs_ty) = rhs.ty() else { unreachable!(); }; assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len()); @@ -3896,18 +4001,15 @@ impl Compiler { self.enum_discriminants.insert(enum_value, retval); retval } - fn compile_stmt_reg( + fn compile_reg( &mut self, - stmt_reg: StmtReg, + clk: CompiledValue, + reset_and_init: Option<(Expr, CompiledValue)>, + source_location: SourceLocation, 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()); + let clk = self.compile_clock(clk, source_location); struct Dispatch; impl ResetTypeDispatch for Dispatch { type Input = (); @@ -3926,18 +4028,15 @@ impl Compiler { 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 reset = if let Some((rst_expr, init)) = reset_and_init { + let rst = self.compile_expr(instantiated_module, Expr::canonical(rst_expr)); + let rst = self.compiled_expr_to_value(rst, source_location); + let rst = self.compiled_value_bool_dest_is_small(rst, source_location); let is_async = R::dispatch((), Dispatch); if is_async { - let cond = Expr::canonical(reg.clock_domain().rst.cast_to(Bool)); + let cond = Expr::canonical(rst_expr.cast_to(Bool)); let cond = self.compile_expr(instantiated_module, cond); - let cond = self.compiled_expr_to_value(cond, reg.source_location()); + let cond = self.compiled_expr_to_value(cond, 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 { @@ -3949,12 +4048,12 @@ impl Compiler { self.compile_simple_connect( [Cond { body: CondBody::IfTrue { cond }, - source_location: reg.source_location(), - }][..] - .intern(), + source_location: source_location, + }] + .intern_slice(), lhs, init, - reg.source_location(), + source_location, ); } Some(RegisterReset { @@ -3969,9 +4068,33 @@ impl Compiler { value, clk_triggered: clk.clk_triggered, reset, - source_location: reg.source_location(), + source_location, }); } + 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 reset_and_init = reg.init().map(|init| { + let init = self.compile_expr(instantiated_module, init); + let init = self.compiled_expr_to_value(init, reg.source_location()); + (reg.clock_domain().rst, init) + }); + self.compile_reg( + clk, + reset_and_init, + reg.source_location(), + instantiated_module, + value, + ); + } fn compile_declaration( &mut self, declaration: StmtDeclaration, @@ -4237,24 +4360,24 @@ impl Compiler { insns.push(end_label.into()); } } - CompiledTypeLayoutBody::Array { element } => { + CompiledTypeLayoutBody::Array { elements_non_empty } => { let CompiledTypeLayoutBody::Array { - element: mask_element, + elements_non_empty: mask_elements_non_empty, } = 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(); + let element_size = elements_non_empty[0].layout.len(); + let mask_element_size = mask_elements_non_empty[0].layout.len(); for element_index in 0..ty.len() { self.compile_memory_port_rw_helper( memory, stride, start, - *element, - *mask_element, + elements_non_empty[element_index], + mask_elements_non_empty[element_index], read.as_mut().map( |MemoryPortReadInsns { addr, @@ -4293,6 +4416,7 @@ impl Compiler { start += element_bit_width; } } + CompiledTypeLayoutBody::PhantomConst => {} CompiledTypeLayoutBody::Bundle { fields } => { let CompiledTypeLayoutBody::Bundle { fields: mask_fields, @@ -4860,6 +4984,88 @@ impl Compiler { } } } + fn compile_extern_module_clock_for_past( + &mut self, + instantiated_module: InstantiatedModule, + clock_for_past: Target, + ) -> ExternModuleClockForPast { + let clock_for_past = TargetInInstantiatedModule { + instantiated_module, + target: clock_for_past, + }; + let clock_for_past = self + .compile_value(clock_for_past) + .map_ty(Clock::from_canonical); + let clock_for_past_debug_name = match clock_for_past + .range + .len() + .as_single() + .expect("Clock is a single slot") + { + TypeLenSingle::BigSlot => { + self.insns + .state_layout + .ty + .big_slots + .debug_data(clock_for_past.range.start().big_slots) + .name + } + TypeLenSingle::SmallSlot => { + self.insns + .state_layout + .ty + .small_slots + .debug_data(clock_for_past.range.start().small_slots) + .name + } + TypeLenSingle::SimOnlySlot => { + unreachable!() + } + }; + let module_prefix = format!("{instantiated_module:?}."); + let trimmed_clock_for_past_debug_name = clock_for_past_debug_name + .strip_prefix(&module_prefix) + .unwrap_or(&clock_for_past_debug_name); + let current_to_past_map = instantiated_module + .leaf_module() + .module_io() + .iter() + .map( + |&AnnotatedModuleIO { + annotations: _, + module_io, + }| { + let target_base = TargetBase::from(module_io); + let current = self.compile_value(TargetInInstantiatedModule { + instantiated_module, + target: target_base.into(), + }); + let unprefixed_layout = CompiledTypeLayout::get(module_io.ty()); + let past_layout = unprefixed_layout.with_prefixed_debug_names(&format!( + "{module_prefix}{:?}$past({trimmed_clock_for_past_debug_name})", + target_base.target_name(), + )); + let past = CompiledValue { + range: self.insns.allocate_variable(&past_layout.layout), + layout: past_layout, + write: Some((current.layout, current.range)), + }; + self.compile_reg::( + clock_for_past, + None, + module_io.source_location(), + instantiated_module, + past, + ); + (current, past) + }, + ) + .collect(); + ExternModuleClockForPast { + clock_for_past, + current_to_past_map, + } + } fn compile_module(&mut self, module: Interned) -> &CompiledModule { let mut trace_decls = Vec::new(); let module_io = module @@ -4888,6 +5094,7 @@ impl Compiler { ModuleBody::Extern(ExternModuleBody { verilog_name: _, parameters: _, + clocks_for_past, simulation, }) => { let Some(simulation) = simulation else { @@ -4904,10 +5111,18 @@ impl Compiler { Target::from(*simulation.sim_io_to_generator_map[&v.module_io.intern()]) }) .collect(); + let clocks_for_past = clocks_for_past + .iter() + .map(|clock_for_past| { + self.compile_extern_module_clock_for_past(*module, *clock_for_past) + }) + .collect(); self.extern_modules.push(CompiledExternModule { module_io_targets, module_io, + clocks_for_past, simulation, + debug_name: format!("{module:?}").intern_deref(), }); } } diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 1a6c2692..2b121b52 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -2,6 +2,7 @@ // See Notices.txt for copyright information use crate::{ + expr::ValueType, int::{BoolOrIntType, SInt, UInt}, intern::{Intern, Interned, Memoize}, sim::interpreter::parts::{ @@ -196,13 +197,27 @@ impl fmt::Debug for Insn { } } -struct PrefixLinesWrapper<'a, W> { +pub(crate) struct PrefixLinesWrapper<'a, W> { writer: W, at_beginning_of_line: bool, blank_line_prefix: &'a str, line_prefix: &'a str, } +impl<'a, W> PrefixLinesWrapper<'a, W> { + pub(crate) fn new(writer: W, at_beginning_of_line: bool, line_prefix: &'a str) -> Self { + Self { + writer, + at_beginning_of_line, + blank_line_prefix: line_prefix.trim_end(), + line_prefix, + } + } + pub(crate) fn into_inner(self) -> W { + self.writer + } +} + impl fmt::Write for PrefixLinesWrapper<'_, T> { fn write_str(&mut self, input: &str) -> fmt::Result { for part in input.split_inclusive('\n') { @@ -239,12 +254,7 @@ impl Insn { if fields.len() == 0 { return Ok(()); } - let mut f = PrefixLinesWrapper { - writer: f, - at_beginning_of_line: false, - blank_line_prefix: "", - line_prefix: " ", - }; + let mut f = PrefixLinesWrapper::new(f, false, " "); writeln!(f, " {{")?; for (field_name, field) in fields { write!(f, "{field_name}: ")?; @@ -320,7 +330,7 @@ impl Insn { } writeln!(f, ",")?; } - write!(f.writer, "}}") + write!(f.into_inner(), "}}") } } diff --git a/crates/fayalite/src/sim/interpreter/parts.rs b/crates/fayalite/src/sim/interpreter/parts.rs index 87321466..75427c9a 100644 --- a/crates/fayalite/src/sim/interpreter/parts.rs +++ b/crates/fayalite/src/sim/interpreter/parts.rs @@ -9,7 +9,7 @@ use crate::{ Insn, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, PrefixLinesWrapper, SmallSInt, SmallUInt, State, }, - value::{DynSimOnlyValue, DynSimOnly}, + value::{DynSimOnly, DynSimOnlyValue}, }, ty::CanonicalType, util::{chain, const_str_cmp}, @@ -435,12 +435,7 @@ impl StatePartIndex { 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, - }; + let mut f = PrefixLinesWrapper::new(f, false, comment_line_start); if let Some(state) = state { f.write_str("(")?; K::debug_fmt_state_value(state, *self, &mut f)?; @@ -453,7 +448,7 @@ impl StatePartIndex { write!(f, "{debug_data:?}")?; } if state.is_some() || debug_data.is_some() { - f.writer.write_str(comment_end)?; + f.into_inner().write_str(comment_end)?; } Ok(()) } diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index 97174178..def2ae32 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -6,7 +6,10 @@ use crate::{ bundle::{Bundle, BundleType}, clock::Clock, enum_::{Enum, EnumType}, - expr::{CastBitsTo, Expr, ToExpr}, + expr::{ + CastBitsTo, Expr, HdlPartialEq, HdlPartialEqImpl, HdlPartialOrd, ToExpr, ValueType, + value_category::{ValueCategorySimValue, ValueCategoryValue}, + }, int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, @@ -23,10 +26,11 @@ 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, + borrow::{Borrow, BorrowMut, Cow}, fmt::{self, Write}, hash::{BuildHasher, Hash, Hasher, RandomState}, - ops::{Deref, DerefMut}, + num::NonZero, + ops::{Deref, DerefMut, Index, IndexMut}, sync::{Arc, Mutex}, }; @@ -136,7 +140,7 @@ impl + Serialize> Serialize for SimValue { S: Serializer, { SerdeSimValue { - ty: SimValue::ty(self), + ty: self.ty(), value: std::borrow::Cow::Borrowed(&*self), } .serialize(serializer) @@ -157,7 +161,22 @@ pub struct SimValue { inner: AlternatingCell>, } -impl Clone for SimValue { +impl ValueType for SimValue { + type Type = T; + type ValueCategory = ValueCategorySimValue; + + fn ty(&self) -> Self::Type { + self.inner.share().ty + } +} + +impl> fmt::Display for SimValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::SimValue::fmt(self, f) + } +} + +impl Clone for SimValue { fn clone(&self) -> Self { Self { inner: AlternatingCell::new_unique(self.inner.share().clone()), @@ -212,9 +231,6 @@ impl SimValue { 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() } @@ -253,7 +269,7 @@ impl SimValue { SimValue::from_opaque(ty.canonical(), opaque) } pub fn canonical(this: &Self) -> SimValue { - SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone()) + SimValue::from_opaque(this.ty().canonical(), Self::opaque(this).clone()) } #[track_caller] pub fn from_dyn_int(v: SimValue) -> Self @@ -274,7 +290,7 @@ impl SimValue { where T: IntType, { - SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone()) + SimValue::from_opaque(this.ty().as_dyn_int(), Self::opaque(&this).clone()) } #[track_caller] pub fn from_bundle(v: SimValue) -> Self @@ -296,7 +312,7 @@ impl SimValue { T: BundleType, { SimValue::from_opaque( - Bundle::from_canonical(Self::ty(this).canonical()), + Bundle::from_canonical(this.ty().canonical()), Self::opaque(&this).clone(), ) } @@ -320,7 +336,7 @@ impl SimValue { T: EnumType, { SimValue::from_opaque( - Enum::from_canonical(Self::ty(this).canonical()), + Enum::from_canonical(this.ty().canonical()), Self::opaque(&this).clone(), ) } @@ -351,8 +367,6 @@ impl fmt::Debug for SimValue { } impl ToExpr for SimValue { - type Type = T; - #[track_caller] fn to_expr(&self) -> Expr { let inner = self.inner.share(); @@ -360,43 +374,297 @@ impl ToExpr for SimValue { inner.sim_only_values_len, 0, "can't convert sim-only values to Expr" ); - inner.opaque.bits().cast_bits_to(inner.ty) + inner.opaque.bits().to_expr().cast_bits_to(inner.ty) } } -pub trait SimValuePartialEq: Type { - #[track_caller] - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool; +impl Index for SimValue> +where + [SimValue]: Index, +{ + type Output = <[SimValue] as Index>::Output; + + fn index(&self, index: I) -> &Self::Output { + (**self).borrow().index(index) + } } -impl, U: Type> PartialEq> for SimValue { +impl IndexMut for SimValue> +where + [SimValue]: IndexMut, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + (**self).borrow_mut().index_mut(index) + } +} + +impl SimValue> { + pub fn iter(&self) -> std::slice::Iter<'_, SimValue> { + (**self).borrow().iter() + } + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, SimValue> { + (**self).borrow_mut().iter_mut() + } +} + +impl IntoIterator for SimValue> { + type Item = SimValue; + type IntoIter = std::vec::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + Vec::into_iter(Self::into_value(self).into()) + } +} + +impl<'a, T: Type, Len: Size> IntoIterator for &'a SimValue> { + type Item = &'a SimValue; + type IntoIter = std::slice::Iter<'a, SimValue>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, T: Type, Len: Size> IntoIterator for &'a mut SimValue> { + type Item = &'a mut SimValue; + type IntoIter = std::slice::IterMut<'a, SimValue>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl AsRef<[SimValue]> for SimValue> { + fn as_ref(&self) -> &[SimValue] { + (**self).as_ref() + } +} + +impl AsMut<[SimValue]> for SimValue> { + fn as_mut(&mut self) -> &mut [SimValue] { + (**self).as_mut() + } +} + +impl PartialEq> for SimValue +where + Self: for<'a> HdlPartialEq<&'a SimValue, Output = SimValue>, +{ #[track_caller] fn eq(&self, other: &SimValue) -> bool { - T::sim_value_eq(self, other) + *self.cmp_eq(other) + } + #[track_caller] + fn ne(&self, other: &SimValue) -> bool { + *self.cmp_ne(other) } } -impl SimValuePartialEq for UIntType { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +pub trait SimValueEq: Type +where + for<'a> SimValue: HdlPartialEq<&'a SimValue, Output = SimValue>, +{ +} + +impl Eq for SimValue where + for<'a> SimValue: HdlPartialEq<&'a SimValue, Output = SimValue> +{ +} + +impl PartialOrd> for SimValue +where + Self: for<'a> HdlPartialOrd<&'a SimValue, Output = SimValue>, +{ + #[track_caller] + fn partial_cmp(&self, other: &SimValue) -> Option { + if *self.cmp_eq(other) { + Some(std::cmp::Ordering::Equal) + } else if *self.cmp_lt(other) { + Some(std::cmp::Ordering::Less) + } else if *self.cmp_gt(other) { + Some(std::cmp::Ordering::Greater) + } else { + None + } + } + + #[track_caller] + fn lt(&self, other: &SimValue) -> bool { + *self.cmp_lt(other) + } + + #[track_caller] + fn le(&self, other: &SimValue) -> bool { + *self.cmp_le(other) + } + + #[track_caller] + fn gt(&self, other: &SimValue) -> bool { + *self.cmp_gt(other) + } + + #[track_caller] + fn ge(&self, other: &SimValue) -> bool { + *self.cmp_ge(other) } } -impl SimValuePartialEq for SIntType { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +pub trait SimValueOrd: SimValueEq +where + for<'a> SimValue: HdlPartialOrd<&'a SimValue, Output = SimValue>, +{ +} + +impl Ord for SimValue +where + for<'a> SimValue: HdlPartialOrd<&'a SimValue, Output = SimValue>, +{ + #[track_caller] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + if *self.cmp_eq(other) { + std::cmp::Ordering::Equal + } else if *self.cmp_lt(other) { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + } } } -impl SimValuePartialEq for Bool { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +impl SimValueEq for UIntType {} + +impl SimValueOrd for UIntType {} + +impl SimValueEq for SIntType {} + +impl SimValueOrd for SIntType {} + +macro_rules! impl_sim_value_cmp_as_bool { + ($ty:ident) => { + impl SimValueEq for $ty {} + + impl SimValueOrd for $ty {} + }; +} + +impl_sim_value_cmp_as_bool!(Bool); +impl_sim_value_cmp_as_bool!(Clock); +impl_sim_value_cmp_as_bool!(Reset); +impl_sim_value_cmp_as_bool!(SyncReset); +impl_sim_value_cmp_as_bool!(AsyncReset); + +#[doc(hidden)] +pub mod match_sim_value { + use crate::{ + sim::value::{SimValue, ToSimValue}, + ty::Type, + }; + + #[doc(hidden)] + pub struct MatchSimValueHelper(Option); + + impl MatchSimValueHelper { + pub fn new(v: T) -> Self { + Self(Some(v)) + } + } + + #[doc(hidden)] + pub trait MatchSimValue { + type MatchValue; + + /// use `self` so it comes first in the method resolution order + fn __fayalite_match_sim_value(self) -> Self::MatchValue + where + Self: Sized; + } + + impl MatchSimValue for MatchSimValueHelper> { + type MatchValue = T::SimValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + SimValue::into_value(self.0.expect("should be Some")) + } + } + + impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a SimValue> { + type MatchValue = &'a T::SimValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + SimValue::value(self.0.expect("should be Some")) + } + } + + impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a mut SimValue> { + type MatchValue = &'a mut T::SimValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + SimValue::value_mut(self.0.expect("should be Some")) + } + } + + impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ &'a T> + where + MatchSimValueHelper<&'a T>: MatchSimValue, + { + type MatchValue = as MatchSimValue>::MatchValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v))) + } + } + + impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ mut &'a T> + where + MatchSimValueHelper<&'a T>: MatchSimValue, + { + type MatchValue = as MatchSimValue>::MatchValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v))) + } + } + + impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a &'_ mut T> + where + MatchSimValueHelper<&'a T>: MatchSimValue, + { + type MatchValue = as MatchSimValue>::MatchValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &**v))) + } + } + + impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a mut &'_ mut T> + where + MatchSimValueHelper<&'a mut T>: MatchSimValue, + { + type MatchValue = as MatchSimValue>::MatchValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &mut **v))) + } + } + + #[doc(hidden)] + pub trait MatchSimValueFallback { + type MatchValue; + + /// use `&mut self` so it comes later in the method resolution order than MatchSimValue + fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue; + } + + impl MatchSimValueFallback for MatchSimValueHelper { + type MatchValue = ::SimValue; + + fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue { + SimValue::into_value(self.0.take().expect("should be Some").into_sim_value()) + } } } -pub trait ToSimValue: ToSimValueWithType<::Type> { - type Type: Type; - +pub trait ToSimValue: ToSimValueWithType<::Type> + ValueType { #[track_caller] fn to_sim_value(&self) -> SimValue; #[track_caller] @@ -438,31 +706,31 @@ pub trait ToSimValueWithType { 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> { + 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); + assert_eq!(retval.ty(), ty); retval } #[track_caller] - fn into_sim_value_with_type(self, ty: ::Type) -> SimValue<::Type> + 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); + assert_eq!(retval.ty(), ty); retval } #[track_caller] - fn arc_into_sim_value_with_type(self: Arc, ty: ::Type) -> SimValue<::Type> { + 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); + assert_eq!(retval.ty(), ty); retval } #[track_caller] - fn arc_to_sim_value_with_type(self: &Arc, ty: ::Type) -> SimValue<::Type> { + 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); + assert_eq!(retval.ty(), ty); retval } } @@ -470,7 +738,6 @@ macro_rules! forward_to_sim_value_with_type { } impl ToSimValue for SimValue { - type Type = T; fn to_sim_value(&self) -> SimValue { self.clone() } @@ -526,9 +793,7 @@ impl ToSimValueWithType for BitSlice { } } -impl ToSimValue for &'_ This { - type Type = This::Type; - +impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This { fn to_sim_value(&self) -> SimValue { This::to_sim_value(self) } @@ -541,8 +806,6 @@ impl, T: Type> ToSimValueWithType for &' } impl ToSimValue for &'_ mut This { - type Type = This::Type; - fn to_sim_value(&self) -> SimValue { This::to_sim_value(self) } @@ -555,8 +818,6 @@ impl, T: Type> ToSimValueWithType for &' } impl ToSimValue for Arc { - type Type = This::Type; - fn to_sim_value(&self) -> SimValue { This::arc_to_sim_value(self) } @@ -577,7 +838,6 @@ impl, T: Type> ToSimValueWithType for Ar impl ToSimValue for crate::intern::Interned { - type Type = This::Type; fn to_sim_value(&self) -> SimValue { This::to_sim_value(self) } @@ -592,8 +852,6 @@ impl + Send + Sync + 'static, T: Type> ToSi } impl ToSimValue for Box { - type Type = This::Type; - fn to_sim_value(&self) -> SimValue { This::to_sim_value(self) } @@ -636,8 +894,6 @@ impl, T: Type> ToSimValueWithType> for [ } 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) @@ -673,8 +929,6 @@ impl, const N: usize> ToSimValue for [Elem where ConstUsize: KnownSize, { - type Type = Array; - fn to_sim_value(&self) -> SimValue { SimValue::from_array_elements(StaticType::TYPE, self) } @@ -728,8 +982,6 @@ impl, T: Type> ToSimValueWithType> for V } 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) } @@ -770,8 +1022,6 @@ impl, T: Type> ToSimValueWithType> for B } 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) } @@ -801,11 +1051,10 @@ impl> ToSimValueWithType ToSimValue for Expr { - type Type = T; #[track_caller] fn to_sim_value(&self) -> SimValue { SimValue::from_bitslice( - Expr::ty(*self), + self.ty(), &crate::expr::ToLiteralBits::to_literal_bits(self) .expect("must be a literal expression"), ) @@ -825,8 +1074,6 @@ macro_rules! impl_to_sim_value_for_bool_like { } impl ToSimValue for bool { - type Type = Bool; - fn to_sim_value(&self) -> SimValue { SimValue::from_value(Bool, *self) } @@ -864,10 +1111,8 @@ impl ToSimValueWithType for bool { } macro_rules! impl_to_sim_value_for_primitive_int { - ($prim:ident) => { + ($prim:ty) => { impl ToSimValue for $prim { - type Type = <$prim as ToExpr>::Type; - #[track_caller] fn to_sim_value( &self, @@ -878,15 +1123,15 @@ macro_rules! impl_to_sim_value_for_primitive_int { forward_to_sim_value_with_type!([] $prim); - impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim { + impl ToSimValueWithType<<<$prim as ValueType>::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> { + ty: <<$prim as ValueType>::Type as IntType>::Dyn, + ) -> SimValue<<<$prim as ValueType>::Type as IntType>::Dyn> { SimValue::from_value( ty, - <<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(), + <<$prim as ValueType>::Type as Type>::SimValue::from(*self).as_dyn_int(), ) } } @@ -894,7 +1139,7 @@ macro_rules! impl_to_sim_value_for_primitive_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); + let ty: <<$prim as ValueType>::Type as IntType>::Dyn = Type::from_canonical(ty); SimValue::into_canonical(self.to_sim_value_with_type(ty)) } } @@ -913,12 +1158,22 @@ 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); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); 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()) } @@ -1279,8 +1534,6 @@ impl ToSimValueWithType> for SimOnlyValue { } impl ToSimValue for DynSimOnlyValue { - type Type = DynSimOnly; - fn to_sim_value(&self) -> SimValue { SimValue::from_value(self.ty(), self.clone()) } @@ -1290,15 +1543,58 @@ impl ToSimValue for DynSimOnlyValue { } } -impl ToSimValue for SimOnlyValue { +impl ValueType for SimOnlyValue { type Type = SimOnly; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + SimOnly::new() + } +} + +impl ToSimValue for SimOnlyValue { fn to_sim_value(&self) -> SimValue { - SimValue::from_value(Default::default(), self.clone()) + SimValue::from_value(self.ty(), self.clone()) } fn into_sim_value(self) -> SimValue { - SimValue::from_value(Default::default(), self) + SimValue::from_value(self.ty(), self) + } +} + +impl HdlPartialEqImpl for DynSimOnly { + #[track_caller] + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, Self::SimValue>, + ) -> bool { + *lhs_value == *rhs_value + } + + #[track_caller] + fn cmp_expr_eq(_lhs: Expr, _rhs: Expr) -> Expr { + panic!("can't compare Expr"); + } +} + +impl, R: SimOnlyValueTrait> HdlPartialEqImpl> + for SimOnly +{ + #[track_caller] + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: SimOnly, + rhs_value: Cow<'_, as Type>::SimValue>, + ) -> bool { + **lhs_value == **rhs_value + } + + #[track_caller] + fn cmp_expr_eq(_lhs: Expr, _rhs: Expr>) -> Expr { + panic!("can't compare Expr>"); } } diff --git a/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs index 98a199c8..2424c03f 100644 --- a/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs +++ b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs @@ -3,6 +3,7 @@ //! `unsafe` parts of [`DynSimOnlyValue`] +use crate::expr::{ValueType, value_category::ValueCategoryValue}; use serde::{Serialize, de::DeserializeOwned}; use std::{ any::{self, TypeId}, @@ -206,9 +207,25 @@ impl Default for SimOnly { } /// 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)] +#[derive(Clone, Eq, Hash, Default, Ord)] pub struct SimOnlyValue(Rc); +impl, U: SimOnlyValueTrait> PartialEq> + for SimOnlyValue +{ + fn eq(&self, other: &SimOnlyValue) -> bool { + >::eq(self, other) + } +} + +impl, U: SimOnlyValueTrait> PartialOrd> + for SimOnlyValue +{ + fn partial_cmp(&self, other: &SimOnlyValue) -> Option { + >::partial_cmp(self, other) + } +} + 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 @@ -279,10 +296,16 @@ impl From> for DynSimOnlyValue { } } -impl DynSimOnlyValue { - pub fn ty(&self) -> DynSimOnly { +impl ValueType for DynSimOnlyValue { + type Type = DynSimOnly; + type ValueCategory = ValueCategoryValue; + + fn ty(&self) -> Self::Type { self.0.ty() } +} + +impl DynSimOnlyValue { pub fn type_id(&self) -> TypeId { self.0.type_id_dyn() } diff --git a/crates/fayalite/src/sim/vcd.rs b/crates/fayalite/src/sim/vcd.rs index e66c3ee2..6ba37b31 100644 --- a/crates/fayalite/src/sim/vcd.rs +++ b/crates/fayalite/src/sim/vcd.rs @@ -6,12 +6,14 @@ use crate::{ expr::Flow, int::UInt, intern::{Intern, Interned}, + prelude::PhantomConst, sim::{ TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceModule, - TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSimOnly, - TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, + TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt, TraceScalar, TraceScalarId, + TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire, TraceWriter, + TraceWriterDecls, time::{SimDuration, SimInstant}, value::DynSimOnlyValue, }, @@ -283,6 +285,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::PhantomConst(v) => v.write_trace(writer, arg), Self::SimOnly(v) => v.write_trace(writer, arg), } } @@ -549,6 +552,33 @@ impl WriteTrace for TraceAsyncReset { } } +impl WriteTrace for TracePhantomConst { + 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 TraceSimOnly { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { let ArgInType { @@ -1091,6 +1121,16 @@ impl TraceWriter for VcdWriter { write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize()) } + fn set_signal_phantom_const( + &mut self, + id: TraceScalarId, + ty: PhantomConst, + ) -> Result<(), Self::Error> { + // avoid multi-line strings because GTKWave can't display them properly: + // https://github.com/gtkwave/gtkwave/issues/460 + write_string_value_change(&mut self.writer, format_args!("{ty:?}"), id.as_usize()) + } + fn set_signal_sim_only_value( &mut self, id: TraceScalarId, diff --git a/crates/fayalite/src/testing.rs b/crates/fayalite/src/testing.rs index b81bc3f1..bc7a0b14 100644 --- a/crates/fayalite/src/testing.rs +++ b/crates/fayalite/src/testing.rs @@ -1,25 +1,54 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - cli::{FormalArgs, FormalMode, FormalOutput, RunPhase}, + build::{ + BaseJobArgs, BaseJobKind, GlobalParams, JobArgsAndDependencies, JobKindAndArgs, JobParams, + NoArgs, RunBuild, + external::{ExternalCommandArgs, ExternalCommandJobKind}, + firrtl::{FirrtlArgs, FirrtlJobKind}, + formal::{Formal, FormalAdditionalArgs, FormalArgs, WriteSbyFileJobKind}, + verilog::{UnadjustedVerilogArgs, VerilogJobArgs, VerilogJobKind}, + }, + bundle::BundleType, firrtl::ExportOptions, + module::Module, util::HashMap, }; -use clap::Parser; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::{ - fmt::Write, + fmt::{self, Write}, path::{Path, PathBuf}, process::Command, sync::{Mutex, OnceLock}, }; -fn assert_formal_helper() -> FormalArgs { - static FORMAL_ARGS: OnceLock = OnceLock::new(); - // ensure we only run parsing once, so errors from env vars don't produce overlapping output if we're called on multiple threads - FORMAL_ARGS - .get_or_init(|| FormalArgs::parse_from(["fayalite::testing::assert_formal"])) - .clone() +#[derive( + clap::ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default, Deserialize, Serialize, +)] +#[non_exhaustive] +pub enum FormalMode { + #[default] + BMC, + Prove, + Live, + Cover, +} + +impl FormalMode { + pub fn as_str(self) -> &'static str { + match self { + FormalMode::BMC => "bmc", + FormalMode::Prove => "prove", + FormalMode::Live => "live", + FormalMode::Cover => "cover", + } + } +} + +impl fmt::Display for FormalMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } } #[derive(Deserialize)] @@ -97,26 +126,99 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { .join(dir) } -#[track_caller] -pub fn assert_formal( - test_name: impl std::fmt::Display, - module: M, - mode: FormalMode, - depth: u64, +fn make_assert_formal_args( + test_name: &dyn std::fmt::Display, + formal_mode: FormalMode, + formal_depth: u64, solver: Option<&str>, export_options: ExportOptions, -) where - FormalArgs: RunPhase, -{ - let mut args = assert_formal_helper(); - args.verilog.firrtl.base.redirect_output_for_rust_test = true; - args.verilog.firrtl.base.output = Some(get_assert_formal_target_path(&test_name)); - args.verilog.firrtl.export_options = export_options; - args.verilog.debug = true; - args.mode = mode; - args.depth = depth; - if let Some(solver) = solver { - args.solver = solver.into(); - } - args.run(module).expect("testing::assert_formal() failed"); +) -> eyre::Result>> { + let args = JobKindAndArgs { + kind: BaseJobKind, + args: BaseJobArgs::from_output_dir_and_env(get_assert_formal_target_path(&test_name), None), + }; + let dependencies = JobArgsAndDependencies { + args, + dependencies: (), + }; + let args = JobKindAndArgs { + kind: FirrtlJobKind, + args: FirrtlArgs { export_options }, + }; + let dependencies = JobArgsAndDependencies { args, dependencies }; + let args = JobKindAndArgs { + kind: ExternalCommandJobKind::new(), + args: ExternalCommandArgs::resolve_program_path( + None, + UnadjustedVerilogArgs { + firtool_extra_args: vec![], + verilog_dialect: None, + verilog_debug: true, + }, + )?, + }; + let dependencies = JobArgsAndDependencies { args, dependencies }; + let args = JobKindAndArgs { + kind: VerilogJobKind, + args: VerilogJobArgs {}, + }; + let dependencies = JobArgsAndDependencies { args, dependencies }; + let args = JobKindAndArgs { + kind: WriteSbyFileJobKind, + args: FormalArgs { + sby_extra_args: vec![], + formal_mode, + formal_depth, + formal_solver: solver.unwrap_or(FormalArgs::DEFAULT_SOLVER).into(), + smtbmc_extra_args: vec![], + }, + }; + let dependencies = JobArgsAndDependencies { args, dependencies }; + let args = JobKindAndArgs { + kind: ExternalCommandJobKind::new(), + args: ExternalCommandArgs::resolve_program_path(None, FormalAdditionalArgs {})?, + }; + Ok(JobArgsAndDependencies { args, dependencies }) +} + +pub fn try_assert_formal>, T: BundleType>( + test_name: impl std::fmt::Display, + module: M, + formal_mode: FormalMode, + formal_depth: u64, + solver: Option<&str>, + export_options: ExportOptions, +) -> eyre::Result<()> { + const APP_NAME: &'static str = "fayalite::testing::assert_formal"; + make_assert_formal_args( + &test_name, + formal_mode, + formal_depth, + solver, + export_options, + )? + .run_without_platform( + |NoArgs {}| Ok(JobParams::new(module)), + &GlobalParams::new(None, APP_NAME), + ) +} + +#[track_caller] +pub fn assert_formal>, T: BundleType>( + test_name: impl std::fmt::Display, + module: M, + formal_mode: FormalMode, + formal_depth: u64, + solver: Option<&str>, + export_options: ExportOptions, +) { + try_assert_formal( + test_name, + module, + formal_mode, + formal_depth, + solver, + export_options, + ) + .expect("testing::assert_formal() failed"); } diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index d9ea6b27..76c09555 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -11,7 +11,7 @@ use crate::{ intern::{Intern, Interned}, phantom_const::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, - sim::value::{DynSimOnlyValue, DynSimOnly, SimValue, ToSimValueWithType}, + sim::value::{DynSimOnly, DynSimOnlyValue, SimValue, ToSimValueWithType}, source_location::SourceLocation, util::{ConstUsize, slice_range, try_slice_range}, }; diff --git a/crates/fayalite/src/ty/serde_impls.rs b/crates/fayalite/src/ty/serde_impls.rs index 1ca916b7..af324f9f 100644 --- a/crates/fayalite/src/ty/serde_impls.rs +++ b/crates/fayalite/src/ty/serde_impls.rs @@ -127,7 +127,7 @@ impl From for CanonicalType { SerdeCanonicalType::Reset => Self::Reset(Reset), SerdeCanonicalType::Clock => Self::Clock(Clock), SerdeCanonicalType::PhantomConst(value) => { - Self::PhantomConst(PhantomConst::new(value.0)) + Self::PhantomConst(PhantomConst::new_interned(value.0)) } SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value), } diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index e85bc9c1..9796488b 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -33,12 +33,15 @@ pub use const_cmp::{ #[doc(inline)] pub use scoped_ref::ScopedRef; -pub(crate) use misc::chain; #[doc(inline)] pub use misc::{ - BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, interned_bit, - iter_eq_by, slice_range, try_slice_range, + BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, + SerdeJsonEscapeIf, SerdeJsonEscapeIfFormatter, SerdeJsonEscapeIfTest, + SerdeJsonEscapeIfTestResult, interned_bit, iter_eq_by, os_str_strip_prefix, + os_str_strip_suffix, serialize_to_json_ascii, serialize_to_json_ascii_pretty, + serialize_to_json_ascii_pretty_with_indent, slice_range, try_slice_range, }; +pub(crate) use misc::{InternedStrCompareAsStr, chain}; pub mod job_server; pub mod prefix_sum; diff --git a/crates/fayalite/src/util/job_server.rs b/crates/fayalite/src/util/job_server.rs index 376ddc0b..e58bba8b 100644 --- a/crates/fayalite/src/util/job_server.rs +++ b/crates/fayalite/src/util/job_server.rs @@ -1,192 +1,156 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use ctor::ctor; -use jobslot::{Acquired, Client}; +use ctor::{ctor, dtor}; +use jobslot::Client; use std::{ ffi::OsString, - mem, + io, mem, num::NonZeroUsize, - sync::{Condvar, Mutex, Once, OnceLock}, - thread::spawn, + sync::{Mutex, MutexGuard}, }; -fn get_or_make_client() -> &'static Client { - #[ctor] - static CLIENT: OnceLock = unsafe { - match Client::from_env() { - Some(client) => OnceLock::from(client), - None => OnceLock::new(), - } - }; +#[ctor] +static CLIENT: Mutex>> = unsafe { Mutex::new(Some(Client::from_env())) }; - CLIENT.get_or_init(|| { - let mut available_parallelism = None; - let mut args = std::env::args_os().skip(1); - while let Some(arg) = args.next() { - const TEST_THREADS_OPTION: &'static [u8] = b"--test-threads"; - if arg.as_encoded_bytes().starts_with(TEST_THREADS_OPTION) { - match arg.as_encoded_bytes().get(TEST_THREADS_OPTION.len()) { - Some(b'=') => { - let mut arg = arg.into_encoded_bytes(); - arg.drain(..=TEST_THREADS_OPTION.len()); - available_parallelism = Some(arg); - break; +#[dtor] +fn drop_client() { + drop( + match CLIENT.lock() { + Ok(v) => v, + Err(e) => e.into_inner(), + } + .take(), + ); +} + +fn get_or_make_client() -> Client { + CLIENT + .lock() + .expect("shouldn't have panicked") + .as_mut() + .expect("shutting down") + .get_or_insert_with(|| { + let mut available_parallelism = None; + let mut args = std::env::args_os().skip(1); + while let Some(arg) = args.next() { + const TEST_THREADS_OPTION: &'static [u8] = b"--test-threads"; + if arg.as_encoded_bytes().starts_with(TEST_THREADS_OPTION) { + match arg.as_encoded_bytes().get(TEST_THREADS_OPTION.len()) { + Some(b'=') => { + let mut arg = arg.into_encoded_bytes(); + arg.drain(..=TEST_THREADS_OPTION.len()); + available_parallelism = Some(arg); + break; + } + None => { + available_parallelism = args.next().map(OsString::into_encoded_bytes); + break; + } + _ => {} } - None => { - available_parallelism = args.next().map(OsString::into_encoded_bytes); - break; - } - _ => {} } } - } - let available_parallelism = if let Some(available_parallelism) = available_parallelism - .as_deref() - .and_then(|v| std::str::from_utf8(v).ok()) - .and_then(|v| v.parse().ok()) - { - available_parallelism - } else if let Ok(available_parallelism) = std::thread::available_parallelism() { - available_parallelism - } else { - NonZeroUsize::new(1).unwrap() - }; - Client::new_with_fifo(available_parallelism.get() - 1).expect("failed to create job server") - }) + let available_parallelism = if let Some(available_parallelism) = available_parallelism + .as_deref() + .and_then(|v| std::str::from_utf8(v).ok()) + .and_then(|v| v.parse().ok()) + { + available_parallelism + } else if let Ok(available_parallelism) = std::thread::available_parallelism() { + available_parallelism + } else { + NonZeroUsize::new(1).unwrap() + }; + Client::new_with_fifo(available_parallelism.get() - 1) + .expect("failed to create job server") + }) + .clone() } struct State { + obtained_count: usize, waiting_count: usize, - available: Vec, - implicit_available: bool, -} - -impl State { - fn total_available(&self) -> usize { - self.available.len() + self.implicit_available as usize - } - fn additional_waiting(&self) -> usize { - self.waiting_count.saturating_sub(self.total_available()) - } } static STATE: Mutex = Mutex::new(State { + obtained_count: 0, waiting_count: 0, - available: Vec::new(), - implicit_available: true, }); -static COND_VAR: Condvar = Condvar::new(); - -#[derive(Debug)] -enum AcquiredJobInner { - FromJobServer(Acquired), - ImplicitJob, -} #[derive(Debug)] pub struct AcquiredJob { - job: AcquiredJobInner, + client: Client, } impl AcquiredJob { - fn start_acquire_thread() { - static STARTED_THREAD: Once = Once::new(); - STARTED_THREAD.call_once(|| { - spawn(|| { - let mut acquired = None; - let client = get_or_make_client(); + pub fn acquire() -> io::Result { + let client = get_or_make_client(); + struct Waiting {} + + impl Waiting { + fn done(self) -> MutexGuard<'static, State> { + mem::forget(self); let mut state = STATE.lock().unwrap(); - loop { - state = if state.additional_waiting() == 0 { - if acquired.is_some() { - drop(state); - drop(acquired.take()); // drop Acquired outside of lock - STATE.lock().unwrap() - } else { - COND_VAR.wait(state).unwrap() - } - } else if acquired.is_some() { - // allocate space before moving Acquired to ensure we - // drop Acquired outside of the lock on panic - state.available.reserve(1); - state.available.push(acquired.take().unwrap()); - COND_VAR.notify_all(); - state - } else { - drop(state); - acquired = Some( - client - .acquire() - .expect("can't acquire token from job server"), - ); - STATE.lock().unwrap() - }; - } - }); - }); - } - fn acquire_inner(block: bool) -> Option { - Self::start_acquire_thread(); - let mut state = STATE.lock().unwrap(); - loop { - if let Some(acquired) = state.available.pop() { - return Some(Self { - job: AcquiredJobInner::FromJobServer(acquired), - }); + state.waiting_count -= 1; + state } - if state.implicit_available { - state.implicit_available = false; - return Some(Self { - job: AcquiredJobInner::ImplicitJob, - }); - } - if !block { - return None; - } - state.waiting_count += 1; - state = COND_VAR.wait(state).unwrap(); - state.waiting_count -= 1; } - } - pub fn try_acquire() -> Option { - Self::acquire_inner(false) - } - pub fn acquire() -> Self { - Self::acquire_inner(true).expect("failed to acquire token") + impl Drop for Waiting { + fn drop(&mut self) { + STATE.lock().unwrap().waiting_count -= 1; + } + } + let mut state = STATE.lock().unwrap(); + if state.obtained_count == 0 && state.waiting_count == 0 { + state.obtained_count = 1; // get implicit token + return Ok(Self { client }); + } + state.waiting_count += 1; + drop(state); + let waiting = Waiting {}; + client.acquire_raw()?; + state = waiting.done(); + state.obtained_count = state + .obtained_count + .checked_add(1) + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "obtained_count overflowed"))?; + drop(state); + Ok(Self { client }) } pub fn run_command( &mut self, cmd: std::process::Command, f: impl FnOnce(&mut std::process::Command) -> std::io::Result, ) -> std::io::Result { - get_or_make_client().configure_make_and_run_with_fifo(cmd, f) + self.client.configure_make_and_run_with_fifo(cmd, f) } } impl Drop for AcquiredJob { fn drop(&mut self) { let mut state = STATE.lock().unwrap(); - match &self.job { - AcquiredJobInner::FromJobServer(_) => { - if state.waiting_count > state.available.len() + state.implicit_available as usize { - // allocate space before moving Acquired to ensure we - // drop Acquired outside of the lock on panic - state.available.reserve(1); - let AcquiredJobInner::FromJobServer(acquired) = - mem::replace(&mut self.job, AcquiredJobInner::ImplicitJob) - else { - unreachable!() - }; - state.available.push(acquired); - COND_VAR.notify_all(); + match &mut *state { + State { + obtained_count: 0, .. + } => unreachable!(), + State { + obtained_count: obtained_count @ 1, + waiting_count, + } => { + *obtained_count = 0; // drop implicit token + let any_waiting = *waiting_count != 0; + drop(state); + if any_waiting { + // we have the implicit token, but some other thread is trying to acquire a token, + // release the implicit token so they can acquire it. + let _ = self.client.release_raw(); // we're in drop, just ignore errors since we at least tried } } - AcquiredJobInner::ImplicitJob => { - state.implicit_available = true; - if state.waiting_count > state.available.len() { - COND_VAR.notify_all(); - } + State { obtained_count, .. } => { + *obtained_count = obtained_count.saturating_sub(1); + drop(state); + let _ = self.client.release_raw(); // we're in drop, just ignore errors since we at least tried } } } diff --git a/crates/fayalite/src/util/misc.rs b/crates/fayalite/src/util/misc.rs index cebbceb5..165ab3a4 100644 --- a/crates/fayalite/src/util/misc.rs +++ b/crates/fayalite/src/util/misc.rs @@ -4,7 +4,9 @@ use crate::intern::{Intern, Interned}; use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView}; use std::{ cell::Cell, + ffi::OsStr, fmt::{self, Debug, Write}, + io, ops::{Bound, Range, RangeBounds}, rc::Rc, sync::{Arc, OnceLock}, @@ -243,3 +245,370 @@ pub fn try_slice_range>(range: R, size: usize) -> Option>(range: R, size: usize) -> Range { try_slice_range(range, size).expect("range out of bounds") } + +pub trait SerdeJsonEscapeIfTest { + fn char_needs_escape(&mut self, ch: char) -> serde_json::Result; +} + +pub trait SerdeJsonEscapeIfTestResult { + fn to_result(self) -> serde_json::Result; +} + +impl SerdeJsonEscapeIfTestResult for bool { + fn to_result(self) -> serde_json::Result { + Ok(self) + } +} + +impl> SerdeJsonEscapeIfTestResult for Result { + fn to_result(self) -> serde_json::Result { + self.map_err(Into::into) + } +} + +impl R, R: SerdeJsonEscapeIfTestResult> SerdeJsonEscapeIfTest for T { + fn char_needs_escape(&mut self, ch: char) -> serde_json::Result { + self(ch).to_result() + } +} + +pub trait SerdeJsonEscapeIfFormatter: serde_json::ser::Formatter { + fn write_unicode_escape(&mut self, writer: &mut W, ch: char) -> io::Result<()> + where + W: ?Sized + io::Write, + { + for utf16 in ch.encode_utf16(&mut [0; 2]) { + write!(writer, "\\u{utf16:04x}")?; + } + Ok(()) + } +} + +impl SerdeJsonEscapeIfFormatter for serde_json::ser::CompactFormatter {} +impl SerdeJsonEscapeIfFormatter for serde_json::ser::PrettyFormatter<'_> {} + +pub struct SerdeJsonEscapeIf { + pub base: Base, + pub test: Test, +} + +impl serde_json::ser::Formatter + for SerdeJsonEscapeIf +{ + fn write_null(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_null(writer) + } + + fn write_bool(&mut self, writer: &mut W, value: bool) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_bool(writer, value) + } + + fn write_i8(&mut self, writer: &mut W, value: i8) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_i8(writer, value) + } + + fn write_i16(&mut self, writer: &mut W, value: i16) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_i16(writer, value) + } + + fn write_i32(&mut self, writer: &mut W, value: i32) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_i32(writer, value) + } + + fn write_i64(&mut self, writer: &mut W, value: i64) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_i64(writer, value) + } + + fn write_i128(&mut self, writer: &mut W, value: i128) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_i128(writer, value) + } + + fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_u8(writer, value) + } + + fn write_u16(&mut self, writer: &mut W, value: u16) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_u16(writer, value) + } + + fn write_u32(&mut self, writer: &mut W, value: u32) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_u32(writer, value) + } + + fn write_u64(&mut self, writer: &mut W, value: u64) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_u64(writer, value) + } + + fn write_u128(&mut self, writer: &mut W, value: u128) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_u128(writer, value) + } + + fn write_f32(&mut self, writer: &mut W, value: f32) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_f32(writer, value) + } + + fn write_f64(&mut self, writer: &mut W, value: f64) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_f64(writer, value) + } + + fn write_number_str(&mut self, writer: &mut W, value: &str) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_number_str(writer, value) + } + + fn begin_string(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.begin_string(writer) + } + + fn end_string(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.end_string(writer) + } + + fn write_string_fragment(&mut self, writer: &mut W, mut fragment: &str) -> io::Result<()> + where + W: ?Sized + io::Write, + { + while let Some((next_escape_index, next_escape_char)) = fragment + .char_indices() + .find_map(|(index, ch)| match self.test.char_needs_escape(ch) { + Ok(false) => None, + Ok(true) => Some(Ok((index, ch))), + Err(e) => Some(Err(e)), + }) + .transpose()? + { + let (no_escapes, rest) = fragment.split_at(next_escape_index); + fragment = &rest[next_escape_char.len_utf8()..]; + self.base.write_string_fragment(writer, no_escapes)?; + self.base.write_unicode_escape(writer, next_escape_char)?; + } + self.base.write_string_fragment(writer, fragment) + } + + fn write_char_escape( + &mut self, + writer: &mut W, + char_escape: serde_json::ser::CharEscape, + ) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_char_escape(writer, char_escape) + } + + fn write_byte_array(&mut self, writer: &mut W, value: &[u8]) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_byte_array(writer, value) + } + + fn begin_array(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.begin_array(writer) + } + + fn end_array(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.end_array(writer) + } + + fn begin_array_value(&mut self, writer: &mut W, first: bool) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.begin_array_value(writer, first) + } + + fn end_array_value(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.end_array_value(writer) + } + + fn begin_object(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.begin_object(writer) + } + + fn end_object(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.end_object(writer) + } + + fn begin_object_key(&mut self, writer: &mut W, first: bool) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.begin_object_key(writer, first) + } + + fn end_object_key(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.end_object_key(writer) + } + + fn begin_object_value(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.begin_object_value(writer) + } + + fn end_object_value(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.end_object_value(writer) + } + + fn write_raw_fragment(&mut self, writer: &mut W, fragment: &str) -> io::Result<()> + where + W: ?Sized + io::Write, + { + self.base.write_raw_fragment(writer, fragment) + } +} + +fn serialize_to_json_ascii_helper( + v: &S, + base: F, +) -> serde_json::Result { + let mut retval = Vec::new(); + v.serialize(&mut serde_json::ser::Serializer::with_formatter( + &mut retval, + SerdeJsonEscapeIf { + base, + test: |ch| ch < '\x20' || ch > '\x7F', + }, + ))?; + String::from_utf8(retval).map_err(|_| serde::ser::Error::custom("invalid UTF-8")) +} + +pub fn serialize_to_json_ascii(v: &T) -> serde_json::Result { + serialize_to_json_ascii_helper(v, serde_json::ser::CompactFormatter) +} + +pub fn serialize_to_json_ascii_pretty( + v: &T, +) -> serde_json::Result { + serialize_to_json_ascii_helper(v, serde_json::ser::PrettyFormatter::new()) +} + +pub fn serialize_to_json_ascii_pretty_with_indent( + v: &T, + indent: &str, +) -> serde_json::Result { + serialize_to_json_ascii_helper( + v, + serde_json::ser::PrettyFormatter::with_indent(indent.as_bytes()), + ) +} + +pub fn os_str_strip_prefix<'a>(os_str: &'a OsStr, prefix: impl AsRef) -> Option<&'a OsStr> { + os_str + .as_encoded_bytes() + .strip_prefix(prefix.as_ref().as_bytes()) + .map(|bytes| { + // Safety: we removed a UTF-8 prefix so bytes starts with a valid boundary + unsafe { OsStr::from_encoded_bytes_unchecked(bytes) } + }) +} + +pub fn os_str_strip_suffix<'a>(os_str: &'a OsStr, suffix: impl AsRef) -> Option<&'a OsStr> { + os_str + .as_encoded_bytes() + .strip_suffix(suffix.as_ref().as_bytes()) + .map(|bytes| { + // Safety: we removed a UTF-8 suffix so bytes ends with a valid boundary + unsafe { OsStr::from_encoded_bytes_unchecked(bytes) } + }) +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub(crate) struct InternedStrCompareAsStr(pub(crate) Interned); + +impl fmt::Debug for InternedStrCompareAsStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Ord for InternedStrCompareAsStr { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + str::cmp(&self.0, &other.0) + } +} + +impl PartialOrd for InternedStrCompareAsStr { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl std::borrow::Borrow for InternedStrCompareAsStr { + fn borrow(&self) -> &str { + &self.0 + } +} diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index ac08a641..a15b8377 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -25,7 +25,7 @@ impl ReadyValid { #[hdl] pub fn firing_data(expr: impl ToExpr) -> Expr> { let expr = expr.to_expr(); - let option_ty = Expr::ty(expr).data; + let option_ty = expr.ty().data; #[hdl] let firing_data = wire(option_ty); connect(firing_data, option_ty.HdlNone()); @@ -42,7 +42,7 @@ impl ReadyValid { ) -> Expr> { let data = HdlOption::map(expr.data, f); #[hdl] - let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]); + let mapped = wire(ReadyValid[data.ty().HdlSome]); connect(mapped.data, data); connect(expr.ready, mapped.ready); mapped @@ -81,7 +81,7 @@ pub fn queue( let count: UInt = m.output(count_ty); #[hdl] - let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); + let inp_index_reg: UInt = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); #[hdl] let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); #[hdl] @@ -212,9 +212,7 @@ pub fn queue( mod tests { use super::*; use crate::{ - cli::FormalMode, firrtl::ExportOptions, - module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal, - ty::StaticType, + firrtl::ExportOptions, module::transform::simplify_enums::SimplifyEnumsKind, ty::StaticType, }; use std::num::NonZero; diff --git a/crates/fayalite/src/vendor.rs b/crates/fayalite/src/vendor.rs new file mode 100644 index 00000000..cdf302d3 --- /dev/null +++ b/crates/fayalite/src/vendor.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +pub mod xilinx; + +pub(crate) fn built_in_job_kinds() -> impl IntoIterator { + xilinx::built_in_job_kinds() +} + +pub(crate) fn built_in_platforms() -> impl IntoIterator { + xilinx::built_in_platforms() +} diff --git a/crates/fayalite/src/vendor/xilinx.rs b/crates/fayalite/src/vendor/xilinx.rs new file mode 100644 index 00000000..d80f3888 --- /dev/null +++ b/crates/fayalite/src/vendor/xilinx.rs @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + annotations::make_annotation_enum, + build::{GlobalParams, ToArgs, WriteArgs}, + intern::Interned, + prelude::{DynPlatform, Platform}, +}; +use clap::ValueEnum; +use ordered_float::NotNan; +use serde::{Deserialize, Serialize}; +use std::fmt; + +pub mod arty_a7; +pub mod primitives; +pub mod yosys_nextpnr_prjxray; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct XdcIOStandardAnnotation { + pub value: Interned, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct XdcLocationAnnotation { + pub location: Interned, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct XdcCreateClockAnnotation { + /// clock period in nanoseconds + pub period: NotNan, +} + +make_annotation_enum! { + #[non_exhaustive] + pub enum XilinxAnnotation { + XdcIOStandard(XdcIOStandardAnnotation), + XdcLocation(XdcLocationAnnotation), + XdcCreateClock(XdcCreateClockAnnotation), + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] +pub struct XilinxArgs { + #[arg(long)] + pub device: Option, +} + +impl XilinxArgs { + pub fn require_device( + &self, + platform: Option<&DynPlatform>, + global_params: &GlobalParams, + ) -> clap::error::Result { + if let Some(device) = self.device { + return Ok(device); + } + if let Some(device) = + platform.and_then(|platform| platform.aspects().get_single_by_type::().copied()) + { + return Ok(device); + } + Err(global_params.clap_error( + clap::error::ErrorKind::MissingRequiredArgument, + "missing --device option", + )) + } +} + +impl ToArgs for XilinxArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + if let Some(device) = self.device { + args.write_long_option_eq("device", device.as_str()); + } + } +} + +macro_rules! make_device_enum { + ($vis:vis enum $Device:ident { + $( + #[ + name = $name:literal, + xray_part = $xray_part:literal, + xray_device = $xray_device:literal, + xray_family = $xray_family:literal, + ] + $variant:ident, + )* + }) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, ValueEnum)] + $vis enum $Device { + $( + #[value(name = $name, alias = $xray_part)] + $variant, + )* + } + + impl $Device { + $vis fn as_str(self) -> &'static str { + match self { + $(Self::$variant => $name,)* + } + } + $vis fn xray_part(self) -> &'static str { + match self { + $(Self::$variant => $xray_part,)* + } + } + $vis fn xray_device(self) -> &'static str { + match self { + $(Self::$variant => $xray_device,)* + } + } + $vis fn xray_family(self) -> &'static str { + match self { + $(Self::$variant => $xray_family,)* + } + } + } + + struct DeviceVisitor; + + impl<'de> serde::de::Visitor<'de> for DeviceVisitor { + type Value = $Device; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("a Xilinx device string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match $Device::from_str(v, false) { + Ok(v) => Ok(v), + Err(_) => Err(E::invalid_value(serde::de::Unexpected::Str(v), &self)), + } + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: serde::de::Error, + { + match str::from_utf8(v).ok().and_then(|v| $Device::from_str(v, false).ok()) { + Some(v) => Ok(v), + None => Err(E::invalid_value(serde::de::Unexpected::Bytes(v), &self)), + } + } + } + + impl<'de> Deserialize<'de> for $Device { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_string(DeviceVisitor) + } + } + + impl Serialize for $Device { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.as_str().serialize(serializer) + } + } + }; +} + +make_device_enum! { + pub enum Device { + #[ + name = "xc7a35ticsg324-1L", + xray_part = "xc7a35tcsg324-1", + xray_device = "xc7a35t", + xray_family = "artix7", + ] + Xc7a35ticsg324_1l, + #[ + name = "xc7a100ticsg324-1L", + xray_part = "xc7a100tcsg324-1", + xray_device = "xc7a100t", + xray_family = "artix7", + ] + Xc7a100ticsg324_1l, + } +} + +impl fmt::Display for Device { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +pub(crate) fn built_in_job_kinds() -> impl IntoIterator { + arty_a7::built_in_job_kinds() + .into_iter() + .chain(yosys_nextpnr_prjxray::built_in_job_kinds()) +} + +pub(crate) fn built_in_platforms() -> impl IntoIterator { + arty_a7::built_in_platforms() + .into_iter() + .chain(yosys_nextpnr_prjxray::built_in_platforms()) +} diff --git a/crates/fayalite/src/vendor/xilinx/arty_a7.rs b/crates/fayalite/src/vendor/xilinx/arty_a7.rs new file mode 100644 index 00000000..552eb4aa --- /dev/null +++ b/crates/fayalite/src/vendor/xilinx/arty_a7.rs @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + intern::{Intern, Interned}, + module::{instance_with_loc, reg_builder_with_loc, wire_with_loc}, + platform::{ + DynPlatform, Peripheral, PeripheralRef, Peripherals, PeripheralsBuilderFactory, + PeripheralsBuilderFinished, Platform, PlatformAspectSet, + peripherals::{ClockInput, Led, RgbLed, Uart}, + }, + prelude::*, + vendor::xilinx::{ + Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, + primitives, + }, +}; +use ordered_float::NotNan; +use std::sync::OnceLock; + +macro_rules! arty_a7_platform { + ( + $vis:vis enum $ArtyA7Platform:ident { + $(#[name = $name:literal, device = $device:ident] + $Variant:ident,)* + } + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + #[non_exhaustive] + $vis enum $ArtyA7Platform { + $($Variant,)* + } + + impl $ArtyA7Platform { + $vis const VARIANTS: &'static [Self] = &[$(Self::$Variant,)*]; + $vis fn device(self) -> Device { + match self { + $(Self::$Variant => Device::$device,)* + } + } + $vis const fn as_str(self) -> &'static str { + match self { + $(Self::$Variant => $name,)* + } + } + fn get_aspects(self) -> &'static PlatformAspectSet { + match self { + $(Self::$Variant => { + static ASPECTS_SET: OnceLock = OnceLock::new(); + ASPECTS_SET.get_or_init(|| self.make_aspects()) + })* + } + } + } + }; +} + +arty_a7_platform! { + pub enum ArtyA7Platform { + #[name = "arty-a7-35t", device = Xc7a35ticsg324_1l] + ArtyA7_35T, + #[name = "arty-a7-100t", device = Xc7a100ticsg324_1l] + ArtyA7_100T, + } +} + +#[derive(Debug)] +pub struct ArtyA7Peripherals { + clk100_div_pow2: [Peripheral; 4], + rst: Peripheral, + rst_sync: Peripheral, + ld0: Peripheral, + ld1: Peripheral, + ld2: Peripheral, + ld3: Peripheral, + ld4: Peripheral, + ld5: Peripheral, + ld6: Peripheral, + ld7: Peripheral, + uart: Peripheral, + // TODO: add rest of peripherals when we need them +} + +impl Peripherals for ArtyA7Peripherals { + fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { + let Self { + clk100_div_pow2, + rst, + rst_sync, + ld0, + ld1, + ld2, + ld3, + ld4, + ld5, + ld6, + ld7, + uart, + } = self; + clk100_div_pow2.append_peripherals(peripherals); + rst.append_peripherals(peripherals); + rst_sync.append_peripherals(peripherals); + ld0.append_peripherals(peripherals); + ld1.append_peripherals(peripherals); + ld2.append_peripherals(peripherals); + ld3.append_peripherals(peripherals); + ld4.append_peripherals(peripherals); + ld5.append_peripherals(peripherals); + ld6.append_peripherals(peripherals); + ld7.append_peripherals(peripherals); + uart.append_peripherals(peripherals); + } +} + +impl ArtyA7Platform { + fn make_aspects(self) -> PlatformAspectSet { + let mut retval = PlatformAspectSet::new(); + retval.insert_new(self.device()); + retval + } +} + +#[hdl_module(extern)] +fn reset_sync() { + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let inp: Bool = m.input(); + #[hdl] + let out: SyncReset = m.output(); + m.annotate_module(BlackBoxInlineAnnotation { + path: "fayalite_arty_a7_reset_sync.v".intern(), + text: r#"module __fayalite_arty_a7_reset_sync(input clk, input inp, output out); + wire reset_0_out; + (* ASYNC_REG = "TRUE" *) + FDPE #( + .INIT(1'b1) + ) reset_0 ( + .Q(reset_0_out), + .C(clk), + .CE(1'b1), + .PRE(inp), + .D(1'b0) + ); + (* ASYNC_REG = "TRUE" *) + FDPE #( + .INIT(1'b1) + ) reset_1 ( + .Q(out), + .C(clk), + .CE(1'b1), + .PRE(inp), + .D(reset_0_out) + ); +endmodule +"# + .intern(), + }); + m.verilog_name("__fayalite_arty_a7_reset_sync"); +} + +impl Platform for ArtyA7Platform { + type Peripherals = ArtyA7Peripherals; + + fn name(&self) -> Interned { + self.as_str().intern() + } + + fn new_peripherals<'builder>( + &self, + builder_factory: PeripheralsBuilderFactory<'builder>, + ) -> (Self::Peripherals, PeripheralsBuilderFinished<'builder>) { + let mut builder = builder_factory.builder(); + + let clk100_div_pow2 = std::array::from_fn(|log2_divisor| { + let divisor = 1u64 << log2_divisor; + let name = if divisor != 1 { + format!("clk100_div_{divisor}") + } else { + "clk100".into() + }; + builder.input_peripheral(name, ClockInput::new(100e6 / divisor as f64)) + }); + builder.add_conflicts(Vec::from_iter(clk100_div_pow2.iter().map(|v| v.id()))); + ( + ArtyA7Peripherals { + clk100_div_pow2, + rst: builder.input_peripheral("rst", Reset), + rst_sync: builder.input_peripheral("rst_sync", SyncReset), + ld0: builder.output_peripheral("ld0", RgbLed), + ld1: builder.output_peripheral("ld1", RgbLed), + ld2: builder.output_peripheral("ld2", RgbLed), + ld3: builder.output_peripheral("ld3", RgbLed), + ld4: builder.output_peripheral("ld4", Led), + ld5: builder.output_peripheral("ld5", Led), + ld6: builder.output_peripheral("ld6", Led), + ld7: builder.output_peripheral("ld7", Led), + uart: builder.output_peripheral("uart", Uart), + }, + builder.finish(), + ) + } + + fn source_location(&self) -> SourceLocation { + SourceLocation::builtin() + } + + fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) { + let ArtyA7Peripherals { + clk100_div_pow2, + rst, + rst_sync, + ld0, + ld1, + ld2, + ld3, + ld4, + ld5, + ld6, + ld7, + uart, + } = peripherals; + let make_buffered_input = |name: &str, location: &str, io_standard: &str, invert: bool| { + let pin = m.input_with_loc(name, SourceLocation::builtin(), Bool); + annotate( + pin, + XdcLocationAnnotation { + location: location.intern(), + }, + ); + annotate( + pin, + XdcIOStandardAnnotation { + value: io_standard.intern(), + }, + ); + let buf = instance_with_loc( + &format!("{name}_buf"), + primitives::IBUF(), + SourceLocation::builtin(), + ); + connect(buf.I, pin); + if invert { !buf.O } else { buf.O } + }; + let make_buffered_output = |name: &str, location: &str, io_standard: &str| { + let pin = m.output_with_loc(name, SourceLocation::builtin(), Bool); + annotate( + pin, + XdcLocationAnnotation { + location: location.intern(), + }, + ); + annotate( + pin, + XdcIOStandardAnnotation { + value: io_standard.intern(), + }, + ); + let buf = instance_with_loc( + &format!("{name}_buf"), + primitives::OBUFT(), + SourceLocation::builtin(), + ); + connect(pin, buf.O); + connect(buf.T, false); + buf.I + }; + let mut frequency = clk100_div_pow2[0].ty().frequency(); + let mut log2_divisor = 0; + let mut clk = None; + for (cur_log2_divisor, p) in clk100_div_pow2.into_iter().enumerate() { + let Some(p) = p.into_used() else { + continue; + }; + debug_assert!( + clk.is_none(), + "conflict-handling logic should ensure at most one clock is used", + ); + frequency = p.ty().frequency(); + clk = Some(p); + log2_divisor = cur_log2_divisor; + } + let clk100_buf = make_buffered_input("clk100", "E3", "LVCMOS33", false); + let startup = instance_with_loc( + "startup", + primitives::STARTUPE2_default_inputs(), + SourceLocation::builtin(), + ); + let clk_global_buf = instance_with_loc( + "clk_global_buf", + primitives::BUFGCE(), + SourceLocation::builtin(), + ); + connect(clk_global_buf.CE, startup.EOS); + let mut clk_global_buf_in = clk100_buf.to_clock(); + for prev_log2_divisor in 0..log2_divisor { + let prev_divisor = 1u64 << prev_log2_divisor; + let clk_in = wire_with_loc( + &format!("clk_div_{prev_divisor}"), + SourceLocation::builtin(), + Clock, + ); + connect(clk_in, clk_global_buf_in); + annotate( + clk_in, + XdcCreateClockAnnotation { + period: NotNan::new(1e9 / (100e6 / prev_divisor as f64)) + .expect("known to be valid"), + }, + ); + annotate(clk_in, DontTouchAnnotation); + let cd = wire_with_loc( + &format!("clk_div_{prev_divisor}_in"), + SourceLocation::builtin(), + ClockDomain[AsyncReset], + ); + connect(cd.clk, clk_in); + connect(cd.rst, (!startup.EOS).to_async_reset()); + let divider = reg_builder_with_loc("divider", SourceLocation::builtin()) + .clock_domain(cd) + .reset(false) + .build(); + connect(divider, !divider); + clk_global_buf_in = divider.to_clock(); + } + connect(clk_global_buf.I, clk_global_buf_in); + let clk_out = wire_with_loc("clk_out", SourceLocation::builtin(), Clock); + connect(clk_out, clk_global_buf.O); + annotate( + clk_out, + XdcCreateClockAnnotation { + period: NotNan::new(1e9 / frequency).expect("known to be valid"), + }, + ); + annotate(clk_out, DontTouchAnnotation); + if let Some(clk) = clk { + connect(clk.instance_io_field().clk, clk_out); + } + let rst_value = { + let rst_buf = make_buffered_input("rst", "C2", "LVCMOS33", true); + let rst_sync = instance_with_loc("rst_sync", reset_sync(), SourceLocation::builtin()); + connect(rst_sync.clk, clk_out); + connect(rst_sync.inp, rst_buf | !startup.EOS); + rst_sync.out + }; + if let Some(rst) = rst.into_used() { + connect(rst.instance_io_field(), rst_value.to_reset()); + } + if let Some(rst_sync) = rst_sync.into_used() { + connect(rst_sync.instance_io_field(), rst_value); + } + let rgb_leds = [ + (ld0, ("G6", "F6", "E1")), + (ld1, ("G3", "J4", "G4")), + (ld2, ("J3", "J2", "H4")), + (ld3, ("K1", "H6", "K2")), + ]; + for (rgb_led, (r_loc, g_loc, b_loc)) in rgb_leds { + let r = make_buffered_output(&format!("{}_r", rgb_led.name()), r_loc, "LVCMOS33"); + let g = make_buffered_output(&format!("{}_g", rgb_led.name()), g_loc, "LVCMOS33"); + let b = make_buffered_output(&format!("{}_b", rgb_led.name()), b_loc, "LVCMOS33"); + if let Some(rgb_led) = rgb_led.into_used() { + connect(r, rgb_led.instance_io_field().r); + connect(g, rgb_led.instance_io_field().g); + connect(b, rgb_led.instance_io_field().b); + } else { + connect(r, false); + connect(g, false); + connect(b, false); + } + } + let leds = [(ld4, "H5"), (ld5, "J5"), (ld6, "T9"), (ld7, "T10")]; + for (led, loc) in leds { + let o = make_buffered_output(&led.name(), loc, "LVCMOS33"); + if let Some(led) = led.into_used() { + connect(o, led.instance_io_field().on); + } else { + connect(o, false); + } + } + let uart_tx = make_buffered_output("uart_tx", "D10", "LVCMOS33"); + let uart_rx = make_buffered_input("uart_rx", "A9", "LVCMOS33", false); + if let Some(uart) = uart.into_used() { + connect(uart_tx, uart.instance_io_field().tx); + connect(uart.instance_io_field().rx, uart_rx); + } else { + connect(uart_tx, true); // idle + } + } + + fn aspects(&self) -> PlatformAspectSet { + self.get_aspects().clone() + } +} + +pub(crate) fn built_in_job_kinds() -> impl IntoIterator { + [] +} + +pub(crate) fn built_in_platforms() -> impl IntoIterator { + ArtyA7Platform::VARIANTS + .iter() + .map(|&v| DynPlatform::new(v)) +} diff --git a/crates/fayalite/src/vendor/xilinx/primitives.rs b/crates/fayalite/src/vendor/xilinx/primitives.rs new file mode 100644 index 00000000..9e22d26f --- /dev/null +++ b/crates/fayalite/src/vendor/xilinx/primitives.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +#![allow(non_snake_case)] + +use crate::prelude::*; + +#[hdl_module(extern)] +pub fn IBUF() { + m.verilog_name("IBUF"); + #[hdl] + let O: Bool = m.output(); + #[hdl] + let I: Bool = m.input(); +} + +#[hdl_module(extern)] +pub fn OBUFT() { + m.verilog_name("OBUFT"); + #[hdl] + let O: Bool = m.output(); + #[hdl] + let I: Bool = m.input(); + #[hdl] + let T: Bool = m.input(); +} + +#[hdl_module(extern)] +pub fn BUFGCE() { + m.verilog_name("BUFGCE"); + #[hdl] + let O: Clock = m.output(); + #[hdl] + let CE: Bool = m.input(); + #[hdl] + let I: Clock = m.input(); +} + +#[hdl_module(extern)] +pub fn STARTUPE2_default_inputs() { + m.verilog_name("STARTUPE2"); + #[hdl] + let CFGCLK: Clock = m.output(); + #[hdl] + let CFGMCLK: Clock = m.output(); + #[hdl] + let EOS: Bool = m.output(); + #[hdl] + let PREQ: Bool = m.output(); +} diff --git a/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs b/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs new file mode 100644 index 00000000..3e1ac0c7 --- /dev/null +++ b/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs @@ -0,0 +1,1043 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + annotations::{Annotation, TargetedAnnotation}, + build::{ + BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams, + JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, + JobKindAndDependencies, ToArgs, WriteArgs, + external::{ + ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, + }, + verilog::{UnadjustedVerilog, VerilogDialect, VerilogJob, VerilogJobKind}, + }, + bundle::{Bundle, BundleType}, + expr::target::{Target, TargetBase}, + firrtl::{ScalarizedModuleABI, ScalarizedModuleABIAnnotations, ScalarizedModuleABIPort}, + intern::{Intern, InternSlice, Interned}, + module::{ + NameId, ScopedNameId, TargetName, + transform::visit::{Visit, Visitor}, + }, + prelude::*, + source_location::SourceLocation, + util::{HashSet, job_server::AcquiredJob}, + vendor::xilinx::{ + Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, + XilinxAnnotation, XilinxArgs, + }, +}; +use eyre::Context; +use serde::{Deserialize, Serialize}; +use std::{ + convert::Infallible, + ffi::{OsStr, OsString}, + fmt::{self, Write}, + ops::ControlFlow, + path::{Path, PathBuf}, +}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)] +pub struct YosysNextpnrXrayWriteYsFileJobKind; + +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] +pub struct YosysNextpnrXrayWriteYsFileArgs {} + +impl ToArgs for YosysNextpnrXrayWriteYsFileArgs { + fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { + let Self {} = self; + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] +pub struct YosysNextpnrXrayWriteYsFile { + main_verilog_file: Interned, + ys_file: Interned, + json_file: Interned, + json_file_name: Interned, +} + +impl YosysNextpnrXrayWriteYsFile { + pub fn main_verilog_file(&self) -> Interned { + self.main_verilog_file + } + pub fn ys_file(&self) -> Interned { + self.ys_file + } + pub fn json_file(&self) -> Interned { + self.json_file + } + pub fn json_file_name(&self) -> Interned { + self.json_file_name + } + fn write_ys( + &self, + output: &mut OsString, + additional_files: &[Interned], + main_module_name_id: NameId, + ) -> eyre::Result<()> { + let Self { + main_verilog_file, + ys_file: _, + json_file: _, + json_file_name, + } = self; + for verilog_file in VerilogJob::all_verilog_files(*main_verilog_file, additional_files)? { + output.push("read_verilog -sv \""); + output.push(verilog_file); + output.push("\"\n"); + } + let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id); + writeln!( + output, + "synth_xilinx -flatten -abc9 -nobram -arch xc7 -top {circuit_name}" + ) + .expect("writing to OsString can't fail"); + output.push("write_json \""); + output.push(json_file_name); + output.push("\"\n"); + Ok(()) + } +} + +impl JobKind for YosysNextpnrXrayWriteYsFileJobKind { + type Args = YosysNextpnrXrayWriteYsFileArgs; + type Job = YosysNextpnrXrayWriteYsFile; + type Dependencies = JobKindAndDependencies; + + fn dependencies(self) -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + mut args: JobArgsAndDependencies, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result> { + args.dependencies + .dependencies + .args + .args + .additional_args + .verilog_dialect + .get_or_insert(VerilogDialect::Yosys); + args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { + let YosysNextpnrXrayWriteYsFileArgs {} = args; + let base_job = dependencies.get_job::(); + let verilog_job = dependencies.get_job::(); + let json_file = base_job.file_with_ext("json"); + Ok(YosysNextpnrXrayWriteYsFile { + main_verilog_file: verilog_job.main_verilog_file(), + ys_file: base_job.file_with_ext("ys"), + json_file, + json_file_name: json_file + .interned_file_name() + .expect("known to have file name"), + }) + }) + } + + fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::DynamicPaths { + source_job_name: VerilogJobKind.name(), + }] + .intern_slice() + } + + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::Path { path: job.ys_file }].intern_slice() + } + + fn name(self) -> Interned { + "yosys-nextpnr-xray-write-ys-file".intern() + } + + fn external_command_params(self, _job: &Self::Job) -> Option { + None + } + + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + params: &JobParams, + _global_params: &GlobalParams, + _acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); + let [additional_files] = inputs else { + unreachable!(); + }; + let additional_files = VerilogJob::unwrap_additional_files(additional_files); + let mut contents = OsString::new(); + job.write_ys( + &mut contents, + additional_files, + params.main_module().name_id(), + )?; + let path = job.ys_file; + std::fs::write(path, contents.as_encoded_bytes()) + .wrap_err_with(|| format!("writing {path:?} failed"))?; + Ok(vec![JobItem::Path { path }]) + } + + fn subcommand_hidden(self) -> bool { + true + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] +pub struct YosysNextpnrXraySynthArgs {} + +impl ToArgs for YosysNextpnrXraySynthArgs { + fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { + let Self {} = self; + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub struct YosysNextpnrXraySynth { + #[serde(flatten)] + write_ys_file: YosysNextpnrXrayWriteYsFile, + ys_file_name: Interned, +} + +impl fmt::Debug for YosysNextpnrXraySynth { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + write_ys_file: + YosysNextpnrXrayWriteYsFile { + main_verilog_file, + ys_file, + json_file, + json_file_name, + }, + ys_file_name, + } = self; + f.debug_struct("YosysNextpnrXraySynth") + .field("main_verilog_file", main_verilog_file) + .field("ys_file", ys_file) + .field("ys_file_name", ys_file_name) + .field("json_file", json_file) + .field("json_file_name", json_file_name) + .finish() + } +} + +impl YosysNextpnrXraySynth { + pub fn main_verilog_file(&self) -> Interned { + self.write_ys_file.main_verilog_file() + } + pub fn ys_file(&self) -> Interned { + self.write_ys_file.ys_file() + } + pub fn ys_file_name(&self) -> Interned { + self.ys_file_name + } + pub fn json_file(&self) -> Interned { + self.write_ys_file.json_file() + } + pub fn json_file_name(&self) -> Interned { + self.write_ys_file.json_file_name() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub struct Yosys; + +impl ExternalProgramTrait for Yosys { + fn default_program_name() -> Interned { + "yosys".intern() + } +} + +impl ExternalCommand for YosysNextpnrXraySynth { + type AdditionalArgs = YosysNextpnrXraySynthArgs; + type AdditionalJobData = Self; + type BaseJobPosition = GetJobPositionDependencies< + GetJobPositionDependencies< + GetJobPositionDependencies<::BaseJobPosition>, + >, + >; + type Dependencies = JobKindAndDependencies; + type ExternalProgram = Yosys; + + fn dependencies() -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + args: JobArgsAndDependencies>, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<( + Self::AdditionalJobData, + ::JobsAndKinds, + )> { + args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + let YosysNextpnrXraySynthArgs {} = args.additional_args; + Ok(Self { + write_ys_file: dependencies.job.job.clone(), + ys_file_name: dependencies + .job + .job + .ys_file() + .interned_file_name() + .expect("known to have file name"), + }) + }) + } + + fn inputs(job: &ExternalCommandJob) -> Interned<[JobItemName]> { + [ + JobItemName::Path { + path: job.additional_job_data().ys_file(), + }, + JobItemName::Path { + path: job.additional_job_data().main_verilog_file(), + }, + JobItemName::DynamicPaths { + source_job_name: VerilogJobKind.name(), + }, + ] + .intern_slice() + } + + fn output_paths(job: &ExternalCommandJob) -> Interned<[Interned]> { + [job.additional_job_data().json_file()].intern_slice() + } + + fn command_line_args(job: &ExternalCommandJob, args: &mut W) { + args.write_arg("-s"); + args.write_interned_arg(job.additional_job_data().ys_file_name()); + } + + fn current_dir(job: &ExternalCommandJob) -> Option> { + Some(job.output_dir()) + } + + fn job_kind_name() -> Interned { + "yosys-nextpnr-xray-synth".intern() + } + + fn subcommand_hidden() -> bool { + true + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)] +pub struct YosysNextpnrXrayWriteXdcFileJobKind; + +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] +pub struct YosysNextpnrXrayWriteXdcFileArgs {} + +impl ToArgs for YosysNextpnrXrayWriteXdcFileArgs { + fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { + let Self {} = self; + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] +pub struct YosysNextpnrXrayWriteXdcFile { + firrtl_export_options: crate::firrtl::ExportOptions, + output_dir: Interned, + xdc_file: Interned, +} + +struct WriteXdcContentsError(eyre::Report); + +impl From for WriteXdcContentsError { + fn from(v: eyre::Report) -> Self { + Self(v) + } +} + +impl From for WriteXdcContentsError { + fn from(_v: fmt::Error) -> Self { + unreachable!("String write can't fail") + } +} + +fn tcl_escape(s: impl AsRef) -> String { + let s = s.as_ref(); + if !s.contains(|ch: char| !ch.is_alphanumeric() && ch != '_') { + return s.into(); + } + let mut retval = String::with_capacity(s.len().saturating_add(2)); + retval.push('"'); + for ch in s.chars() { + if let '$' | '\\' | '[' = ch { + retval.push('\\'); + } + retval.push(ch); + } + retval.push('"'); + retval +} + +#[derive(Copy, Clone, Debug)] +enum AnnotationTarget { + None, + Module(Module), + Mem(Mem), + Target(Interned), +} + +impl AnnotationTarget { + fn source_location(self) -> SourceLocation { + match self { + AnnotationTarget::None => unreachable!(), + AnnotationTarget::Module(module) => module.source_location(), + AnnotationTarget::Mem(mem) => mem.source_location(), + AnnotationTarget::Target(target) => target.base().source_location(), + } + } +} + +struct XdcFileWriter { + output: W, + module_depth: usize, + annotation_target: AnnotationTarget, + dont_touch_targets: HashSet>, + required_dont_touch_targets: HashSet>, +} + +impl XdcFileWriter { + fn run(output: W, top_module: Module) -> Result<(), WriteXdcContentsError> { + let mut this = Self { + output, + module_depth: 0, + annotation_target: AnnotationTarget::None, + dont_touch_targets: HashSet::default(), + required_dont_touch_targets: HashSet::default(), + }; + top_module.visit(&mut this)?; + let Self { + output: _, + module_depth: _, + annotation_target: _, + dont_touch_targets, + required_dont_touch_targets, + } = this; + for &target in required_dont_touch_targets.difference(&dont_touch_targets) { + return Err(eyre::eyre!( + "a DontTouchAnnotation is required since the target is also annotated with a XilinxAnnotation:\ntarget: {target:?}\nat: {}", + target.base().source_location(), + ).into()); + } + Ok(()) + } + fn default_visit_with>( + &mut self, + module_depth: usize, + annotation_target: AnnotationTarget, + v: &T, + ) -> Result<(), WriteXdcContentsError> { + let Self { + output: _, + module_depth: old_module_depth, + annotation_target: old_annotation_target, + dont_touch_targets: _, + required_dont_touch_targets: _, + } = *self; + self.module_depth = module_depth; + self.annotation_target = annotation_target; + let retval = v.default_visit(self); + self.module_depth = old_module_depth; + self.annotation_target = old_annotation_target; + retval + } +} + +impl Visitor for XdcFileWriter { + type Error = WriteXdcContentsError; + + fn visit_targeted_annotation(&mut self, v: &TargetedAnnotation) -> Result<(), Self::Error> { + self.default_visit_with(self.module_depth, AnnotationTarget::Target(v.target()), v) + } + + fn visit_module(&mut self, v: &Module) -> Result<(), Self::Error> { + self.default_visit_with( + self.module_depth + 1, + AnnotationTarget::Module(v.canonical()), + v, + ) + } + + fn visit_mem( + &mut self, + v: &Mem, + ) -> Result<(), Self::Error> + where + Element: Visit, + { + self.default_visit_with( + self.module_depth + 1, + AnnotationTarget::Mem(v.canonical()), + v, + ) + } + + fn visit_dont_touch_annotation(&mut self, _v: &DontTouchAnnotation) -> Result<(), Self::Error> { + if let AnnotationTarget::Target(target) = self.annotation_target { + self.dont_touch_targets.insert(target); + } + Ok(()) + } + + fn visit_xilinx_annotation(&mut self, v: &XilinxAnnotation) -> Result<(), Self::Error> { + fn todo( + msg: &str, + annotation: &XilinxAnnotation, + source_location: SourceLocation, + ) -> Result { + Err(WriteXdcContentsError(eyre::eyre!( + "{msg}\nannotation: {annotation:?}\nat: {source_location}" + ))) + } + if self.module_depth != 1 { + match todo( + "annotations are not yet supported outside of the top module since the logic to figure out the correct name isn't implemented", + v, + self.annotation_target.source_location(), + )? {} + } + match self.annotation_target { + AnnotationTarget::None => unreachable!(), + AnnotationTarget::Module(module) => match v { + XilinxAnnotation::XdcIOStandard(_) + | XilinxAnnotation::XdcLocation(_) + | XilinxAnnotation::XdcCreateClock(_) => { + return Err(WriteXdcContentsError(eyre::eyre!( + "annotation not allowed on a module: {v:?}\nat: {}", + module.source_location(), + ))); + } + }, + AnnotationTarget::Mem(mem) => match todo( + "xilinx annotations are not yet supported on memories since the logic to figure out the correct name isn't implemented", + v, + mem.source_location(), + )? {}, + AnnotationTarget::Target(target) => { + let base = target.base(); + match *base { + TargetBase::ModuleIO(_) => { + // already handled by write_xdc_contents handling the main module's ScalarizedModuleABI + Ok(()) + } + TargetBase::MemPort(mem_port) => { + match todo( + "xilinx annotations are not yet supported on memory ports since the logic to figure out the correct name isn't implemented", + v, + mem_port.source_location(), + )? {} + } + TargetBase::Reg(_) + | TargetBase::RegSync(_) + | TargetBase::RegAsync(_) + | TargetBase::Wire(_) => { + match *target { + Target::Base(_) => {} + Target::Child(_) => match todo( + "xilinx annotations are not yet supported on parts of registers/wires since the logic to figure out the correct name isn't implemented", + v, + base.source_location(), + )? {}, + } + match base.canonical_ty() { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => {} + CanonicalType::Enum(_) + | CanonicalType::Array(_) + | CanonicalType::Bundle(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => match todo( + "xilinx annotations are not yet supported on types other than integers, Bool, resets, or Clock since the logic to figure out the correct name isn't implemented", + v, + base.source_location(), + )? {}, + } + self.required_dont_touch_targets.insert(target); + match v { + XilinxAnnotation::XdcIOStandard(_) + | XilinxAnnotation::XdcLocation(_) => { + return Err(WriteXdcContentsError(eyre::eyre!( + "annotation must be on a ModuleIO: {v:?}\nat: {}", + base.source_location(), + ))); + } + XilinxAnnotation::XdcCreateClock(XdcCreateClockAnnotation { + period, + }) => { + let TargetName(ScopedNameId(_, NameId(name, _)), _) = + base.target_name(); + writeln!( + self.output, + "create_clock -period {period} [get_nets {}]", + tcl_escape(name), + )?; + Ok(()) + } + } + } + TargetBase::Instance(instance) => match todo( + "xilinx annotations are not yet supported on instances' IO since the logic to figure out the correct name isn't implemented", + v, + instance.source_location(), + )? {}, + } + } + } + } +} + +impl YosysNextpnrXrayWriteXdcFile { + fn write_xdc_contents_for_port_and_annotations( + &self, + output: &mut impl fmt::Write, + port: &ScalarizedModuleABIPort, + annotations: ScalarizedModuleABIAnnotations<'_>, + ) -> Result<(), WriteXdcContentsError> { + for annotation in annotations { + match annotation.annotation() { + Annotation::DontTouch(_) + | Annotation::SVAttribute(_) + | Annotation::BlackBoxInline(_) + | Annotation::BlackBoxPath(_) + | Annotation::DocString(_) + | Annotation::CustomFirrtl(_) => {} + Annotation::Xilinx(XilinxAnnotation::XdcLocation(XdcLocationAnnotation { + location, + })) => writeln!( + output, + "set_property LOC {} [get_ports {}]", + tcl_escape(location), + tcl_escape(port.scalarized_name()), + )?, + Annotation::Xilinx(XilinxAnnotation::XdcIOStandard(XdcIOStandardAnnotation { + value, + })) => writeln!( + output, + "set_property IOSTANDARD {} [get_ports {}]", + tcl_escape(value), + tcl_escape(port.scalarized_name()), + )?, + Annotation::Xilinx(XilinxAnnotation::XdcCreateClock( + XdcCreateClockAnnotation { period }, + )) => writeln!( + output, + "create_clock -period {period} [get_ports {}]", + tcl_escape(port.scalarized_name()), + )?, + } + } + Ok(()) + } + fn write_xdc_contents( + &self, + output: &mut String, + top_module: &Module, + ) -> eyre::Result<()> { + let scalarized_module_abi = + ScalarizedModuleABI::new(top_module, self.firrtl_export_options) + .map_err(eyre::Report::from)?; + match scalarized_module_abi.for_each_port_and_annotations(|port, annotations| { + match self.write_xdc_contents_for_port_and_annotations(output, port, annotations) { + Ok(()) => ControlFlow::Continue(()), + Err(e) => ControlFlow::Break(e), + } + }) { + ControlFlow::Continue(()) => {} + ControlFlow::Break(e) => return Err(e.0), + } + XdcFileWriter::run(output, *top_module).map_err(|e| e.0) + } +} + +impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind { + type Args = YosysNextpnrXrayWriteXdcFileArgs; + type Job = YosysNextpnrXrayWriteXdcFile; + type Dependencies = JobKindAndDependencies>; + + fn dependencies(self) -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + args: JobArgsAndDependencies, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result> { + let firrtl_export_options = args + .dependencies + .dependencies + .dependencies + .dependencies + .dependencies + .args + .args + .export_options; + args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { + let YosysNextpnrXrayWriteXdcFileArgs {} = args; + let base_job = dependencies.get_job::(); + Ok(YosysNextpnrXrayWriteXdcFile { + firrtl_export_options, + output_dir: base_job.output_dir(), + xdc_file: base_job.file_with_ext("xdc"), + }) + }) + } + + fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::Path { + path: job.output_dir, + }] + .intern_slice() + } + + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + [JobItemName::Path { path: job.xdc_file }].intern_slice() + } + + fn name(self) -> Interned { + "yosys-nextpnr-xray-write-xdc-file".intern() + } + + fn external_command_params(self, _job: &Self::Job) -> Option { + None + } + + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + params: &JobParams, + _global_params: &GlobalParams, + _acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); + let mut xdc = String::new(); + job.write_xdc_contents(&mut xdc, params.main_module())?; + std::fs::write(job.xdc_file, xdc)?; + Ok(vec![JobItem::Path { path: job.xdc_file }]) + } + + fn subcommand_hidden(self) -> bool { + true + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub struct NextpnrXilinx; + +impl ExternalProgramTrait for NextpnrXilinx { + fn default_program_name() -> Interned { + "nextpnr-xilinx".intern() + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] +pub struct YosysNextpnrXrayRunNextpnrArgs { + #[command(flatten)] + pub common: XilinxArgs, + #[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)] + pub nextpnr_xilinx_chipdb_dir: PathBuf, + #[arg(long, default_value_t = 0)] + pub nextpnr_xilinx_seed: i32, +} + +impl ToArgs for YosysNextpnrXrayRunNextpnrArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { + common, + nextpnr_xilinx_chipdb_dir, + nextpnr_xilinx_seed, + } = self; + common.to_args(args); + args.write_long_option_eq("nextpnr-xilinx-chipdb-dir", nextpnr_xilinx_chipdb_dir); + args.write_display_arg(format_args!("--nextpnr-xilinx-seed={nextpnr_xilinx_seed}")); + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] +pub struct YosysNextpnrXrayRunNextpnr { + nextpnr_xilinx_chipdb_dir: Interned, + device: Device, + nextpnr_xilinx_seed: i32, + xdc_file: Interned, + xdc_file_name: Interned, + json_file: Interned, + json_file_name: Interned, + routed_json_file: Interned, + routed_json_file_name: Interned, + fasm_file: Interned, + fasm_file_name: Interned, +} + +impl YosysNextpnrXrayRunNextpnr { + fn chipdb_file(&self) -> Interned { + let mut retval = self + .nextpnr_xilinx_chipdb_dir + .join(self.device.xray_device()); + retval.set_extension("bin"); + retval.intern_deref() + } +} + +impl ExternalCommand for YosysNextpnrXrayRunNextpnr { + type AdditionalArgs = YosysNextpnrXrayRunNextpnrArgs; + type AdditionalJobData = Self; + type BaseJobPosition = GetJobPositionDependencies< + GetJobPositionDependencies<::BaseJobPosition>, + >; + type Dependencies = JobKindAndDependencies; + type ExternalProgram = NextpnrXilinx; + + fn dependencies() -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + args: JobArgsAndDependencies>, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<( + Self::AdditionalJobData, + ::JobsAndKinds, + )> { + args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + let YosysNextpnrXrayRunNextpnrArgs { + common, + nextpnr_xilinx_chipdb_dir, + nextpnr_xilinx_seed, + } = args.additional_args; + let base_job = dependencies.get_job::(); + let write_xdc_file = dependencies.get_job::(); + let synth = dependencies.get_job::, _>(); + let routed_json_file = base_job.file_with_ext("routed.json"); + let fasm_file = base_job.file_with_ext("fasm"); + Ok(Self { + nextpnr_xilinx_chipdb_dir: nextpnr_xilinx_chipdb_dir.intern_deref(), + device: common.require_device(base_job.platform(), global_params)?, + nextpnr_xilinx_seed, + xdc_file: write_xdc_file.xdc_file, + xdc_file_name: write_xdc_file + .xdc_file + .interned_file_name() + .expect("known to have file name"), + json_file: synth.additional_job_data().json_file(), + json_file_name: synth.additional_job_data().json_file_name(), + routed_json_file, + routed_json_file_name: routed_json_file + .interned_file_name() + .expect("known to have file name"), + fasm_file, + fasm_file_name: fasm_file + .interned_file_name() + .expect("known to have file name"), + }) + }) + } + + fn inputs(job: &ExternalCommandJob) -> Interned<[JobItemName]> { + [ + JobItemName::Path { + path: job.additional_job_data().json_file, + }, + JobItemName::Path { + path: job.additional_job_data().xdc_file, + }, + ] + .intern_slice() + } + + fn output_paths(job: &ExternalCommandJob) -> Interned<[Interned]> { + [ + job.additional_job_data().routed_json_file, + job.additional_job_data().fasm_file, + ] + .intern_slice() + } + + fn command_line_args(job: &ExternalCommandJob, args: &mut W) { + let job_data @ YosysNextpnrXrayRunNextpnr { + nextpnr_xilinx_seed, + xdc_file_name, + json_file_name, + routed_json_file_name, + fasm_file_name, + .. + } = job.additional_job_data(); + args.write_long_option_eq("chipdb", job_data.chipdb_file()); + args.write_long_option_eq("xdc", xdc_file_name); + args.write_long_option_eq("json", json_file_name); + args.write_long_option_eq("write", routed_json_file_name); + args.write_long_option_eq("fasm", fasm_file_name); + args.write_display_arg(format_args!("--seed={nextpnr_xilinx_seed}")); + } + + fn current_dir(job: &ExternalCommandJob) -> Option> { + Some(job.output_dir()) + } + + fn job_kind_name() -> Interned { + "yosys-nextpnr-xray-run-nextpnr".intern() + } + + fn subcommand_hidden() -> bool { + true + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub struct Xcfasm; + +impl ExternalProgramTrait for Xcfasm { + fn default_program_name() -> Interned { + "xcfasm".intern() + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] +pub struct YosysNextpnrXrayArgs { + #[arg(long, env = "DB_DIR", value_hint = clap::ValueHint::DirPath)] + pub prjxray_db_dir: PathBuf, +} + +impl ToArgs for YosysNextpnrXrayArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { prjxray_db_dir } = self; + args.write_long_option_eq("prjxray-db-dir", prjxray_db_dir); + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] +pub struct YosysNextpnrXray { + prjxray_db_dir: Interned, + device: Device, + fasm_file: Interned, + fasm_file_name: Interned, + frames_file: Interned, + frames_file_name: Interned, + bit_file: Interned, + bit_file_name: Interned, +} + +impl YosysNextpnrXray { + fn db_root(&self) -> Interned { + self.prjxray_db_dir + .join(self.device.xray_family()) + .intern_deref() + } + fn part_file(&self) -> Interned { + let mut retval = self.prjxray_db_dir.join(self.device.xray_family()); + retval.push(self.device.xray_part()); + retval.push("part.yaml"); + retval.intern_deref() + } +} + +impl ExternalCommand for YosysNextpnrXray { + type AdditionalArgs = YosysNextpnrXrayArgs; + type AdditionalJobData = Self; + type BaseJobPosition = GetJobPositionDependencies< + ::BaseJobPosition, + >; + type Dependencies = JobKindAndDependencies>; + type ExternalProgram = Xcfasm; + + fn dependencies() -> Self::Dependencies { + Default::default() + } + + fn args_to_jobs( + args: JobArgsAndDependencies>, + params: &JobParams, + global_params: &GlobalParams, + ) -> eyre::Result<( + Self::AdditionalJobData, + ::JobsAndKinds, + )> { + args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + let YosysNextpnrXrayArgs { prjxray_db_dir } = args.additional_args; + let base_job = dependencies.get_job::(); + let frames_file = base_job.file_with_ext("frames"); + let bit_file = base_job.file_with_ext("bit"); + Ok(Self { + prjxray_db_dir: prjxray_db_dir.intern_deref(), + device: dependencies.job.job.additional_job_data().device, + fasm_file: dependencies.job.job.additional_job_data().fasm_file, + fasm_file_name: dependencies.job.job.additional_job_data().fasm_file_name, + frames_file, + frames_file_name: frames_file + .interned_file_name() + .expect("known to have file name"), + bit_file, + bit_file_name: bit_file + .interned_file_name() + .expect("known to have file name"), + }) + }) + } + + fn inputs(job: &ExternalCommandJob) -> Interned<[JobItemName]> { + [JobItemName::Path { + path: job.additional_job_data().fasm_file, + }] + .intern_slice() + } + + fn output_paths(job: &ExternalCommandJob) -> Interned<[Interned]> { + [ + job.additional_job_data().frames_file, + job.additional_job_data().bit_file, + ] + .intern_slice() + } + + fn command_line_args(job: &ExternalCommandJob, args: &mut W) { + let job_data @ YosysNextpnrXray { + device, + fasm_file_name, + frames_file_name, + bit_file_name, + .. + } = job.additional_job_data(); + args.write_arg("--sparse"); + args.write_long_option_eq("db-root", job_data.db_root()); + args.write_long_option_eq("part", device.xray_part()); + args.write_long_option_eq("part_file", job_data.part_file()); + args.write_long_option_eq("fn_in", fasm_file_name); + args.write_long_option_eq("frm_out", frames_file_name); + args.write_long_option_eq("bit_out", bit_file_name); + } + + fn current_dir(job: &ExternalCommandJob) -> Option> { + Some(job.output_dir()) + } + + fn job_kind_name() -> Interned { + "yosys-nextpnr-xray".intern() + } +} + +pub(crate) fn built_in_job_kinds() -> impl IntoIterator { + [ + DynJobKind::new(YosysNextpnrXrayWriteYsFileJobKind), + DynJobKind::new(ExternalCommandJobKind::::new()), + DynJobKind::new(YosysNextpnrXrayWriteXdcFileJobKind), + DynJobKind::new(ExternalCommandJobKind::::new()), + DynJobKind::new(ExternalCommandJobKind::::new()), + ] +} + +pub(crate) fn built_in_platforms() -> impl IntoIterator { + [] +} diff --git a/crates/fayalite/src/wire.rs b/crates/fayalite/src/wire.rs index 51f8cf7d..a350d9a4 100644 --- a/crates/fayalite/src/wire.rs +++ b/crates/fayalite/src/wire.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - expr::{Expr, Flow, ToExpr}, + expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr}, intern::Interned, module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire}, source_location::SourceLocation, @@ -16,7 +16,16 @@ pub struct Wire { ty: T, } -impl fmt::Debug for Wire { +impl ValueType for Wire { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl fmt::Debug for Wire { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Wire({:?}: ", self.name)?; self.ty.fmt(f)?; @@ -49,9 +58,6 @@ impl Wire { ty: T::from_canonical(ty), } } - pub fn ty(&self) -> T { - self.ty - } pub fn new_unchecked( scoped_name: ScopedNameId, source_location: SourceLocation, diff --git a/crates/fayalite/tests/formal.rs b/crates/fayalite/tests/formal.rs index 65264dc1..cb78d1d4 100644 --- a/crates/fayalite/tests/formal.rs +++ b/crates/fayalite/tests/formal.rs @@ -2,19 +2,7 @@ // See Notices.txt for copyright information //! Formal tests in Fayalite -use fayalite::{ - cli::FormalMode, - clock::{Clock, ClockDomain}, - expr::{CastTo, HdlPartialEq}, - firrtl::ExportOptions, - formal::{any_const, any_seq, formal_reset, hdl_assert, hdl_assume}, - hdl, hdl_module, - int::{Bool, DynSize, Size, UInt, UIntType}, - module::{connect, connect_any, instance, memory, reg_builder, wire}, - reset::ToReset, - testing::assert_formal, - ty::StaticType, -}; +use fayalite::prelude::*; /// Test hidden state /// @@ -119,7 +107,7 @@ mod hidden_state { FormalMode::Prove, 16, None, - ExportOptions::default(), + Default::default(), ); // here a couple of cycles is enough assert_formal( @@ -128,7 +116,7 @@ mod hidden_state { FormalMode::Prove, 2, None, - ExportOptions::default(), + Default::default(), ); } } @@ -242,7 +230,7 @@ mod memory { #[hdl] let wr: WritePort = wire(WritePort[n]); connect(wr.addr, any_seq(UInt[n])); - connect(wr.data, any_seq(UInt::<8>::TYPE)); + connect(wr.data, any_seq(UInt::<8>::new_static())); connect(wr.en, any_seq(Bool)); #[hdl] let dut = instance(example_sram(n)); @@ -289,7 +277,7 @@ mod memory { FormalMode::Prove, 2, None, - ExportOptions::default(), + Default::default(), ); } } diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index 8802fd4b..50302827 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -4,7 +4,6 @@ use fayalite::{ bundle::BundleType, enum_::EnumType, int::{BoolOrIntType, IntType}, - phantom_const::PhantomConst, prelude::*, ty::StaticType, }; @@ -197,3 +196,51 @@ check_bounds!(CheckBoundsTTT2<#[a, Type] A: BundleType +, #[b, Type] B: Type +, check_bounds!(CheckBoundsTTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Type] C: Type +>); check_bounds!(CheckBoundsTTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>); check_bounds!(CheckBoundsTTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Type] C: Type +>); + +#[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)] +pub struct MyPhantomConstInner { + pub a: usize, + pub b: UInt, +} + +#[hdl(outline_generated, get(|v| v.a))] +pub type GetA> = DynSize; + +#[hdl(outline_generated, get(|v| v.b))] +pub type GetB> = UInt; + +#[hdl(outline_generated, no_static)] +pub struct MyTypeWithPhantomConstParameter> { + pub a: ArrayType>, + pub b: HdlOption>, +} + +#[hdl(outline_generated)] +struct MyPrivateType {} + +#[hdl(outline_generated)] +pub(crate) struct MyPubCrateType {} + +#[hdl(outline_generated)] +pub struct MyTypeWithPrivateMembers { + a: MyPrivateType, + pub(crate) b: MyPubCrateType, + pub c: Bool, +} + +#[hdl(outline_generated)] +struct MyPrivateTypeWithArg { + v: T, +} + +#[hdl(outline_generated)] +pub(crate) struct MyPubCrateTypeWithArg { + v: T, +} + +#[hdl(outline_generated)] +pub struct MyTypeWithPrivateMembersWithArg { + a: MyPrivateTypeWithArg, + pub(crate) b: MyPubCrateTypeWithArg, + pub c: T, +} diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index c2dc24ea..2761cbae 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -6,6 +6,7 @@ use fayalite::{ int::{UIntInRange, UIntInRangeInclusive}, intern::Intern, module::transform::simplify_enums::SimplifyEnumsKind, + platform::PlatformIOBuilder, prelude::*, reset::ResetType, ty::StaticType, @@ -214,7 +215,7 @@ where let o: Array = m.output(); let bytes = v.to_string().as_bytes().to_expr(); #[hdl] - let o2: Array> = m.output(Expr::ty(bytes)); + let o2: Array> = m.output(bytes.ty()); connect( o, #[hdl] @@ -1175,7 +1176,7 @@ pub fn check_memory_init() { let waddr2: UInt<4> = m.input(); #[hdl] let wdata2: UInt<31> = m.input(); - let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static())); + let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static::>())); #[hdl] let mut mem2 = memory_with_init(mem_init2); let read_port2 = mem2.new_read_port(); @@ -1783,7 +1784,7 @@ pub fn check_memory_of_bundle_of_arrays() { [(i * i).wrapping_mul(2), (i * i).wrapping_mul(3)], [(i * i).wrapping_mul(i), i * 2], ], - i.cast_to_static(), + i.cast_to_static::>(), ) })); #[hdl] @@ -1953,9 +1954,9 @@ pub fn check_memory_of_array_of_bundle() { let clk: Clock = m.input(); let mem_init = Vec::from_iter((0..0x10u8).map(|i| { [ - (i, i.cast_to_static()), - ((i * i), (i / 2).cast_to_static()), - ((i * i).wrapping_mul(i), (i / 4).cast_to_static()), + (i, i.cast_to_static::>()), + ((i * i), (i / 2).cast_to_static::>()), + ((i * i).wrapping_mul(i), (i / 4).cast_to_static::>()), ] })); #[hdl] @@ -2268,7 +2269,8 @@ pub fn check_memory_of_bundle() { let wmask: (Bool, Bool) = m.input(); #[hdl] let clk: Clock = m.input(); - let mem_init = Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static()))); + let mem_init = + Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static::>()))); #[hdl] let mut mem = memory_with_init(mem_init); let read_port = mem.new_read_port(); @@ -4631,3 +4633,55 @@ circuit check_uint_in_range: ", }; } + +#[hdl_module(outline_generated)] +pub fn check_platform_io(platform_io_builder: PlatformIOBuilder<'_>) { + #[hdl] + let io = m.add_platform_io(platform_io_builder); +} + +#[cfg(todo)] +#[test] +fn test_platform_io() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_platform_io(todo!()); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_platform_io.fir": r"FIRRTL version 3.2.0 +circuit check_platform_io: + 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_platform_io: @[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 b9c6a80b..cbe0b58b 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -3,7 +3,7 @@ use fayalite::{ memory::{ReadStruct, ReadWriteStruct, WriteStruct}, - module::{instance_with_loc, reg_builder_with_loc}, + module::{instance_with_loc, memory_with_init_and_loc, reg_builder_with_loc}, prelude::*, reset::ResetType, sim::vcd::VcdWriterDecls, @@ -86,7 +86,7 @@ pub fn mod1() { #[hdl] let child = instance(mod1_child()); #[hdl] - let o: mod1_child = m.output(Expr::ty(child)); + let o: mod1_child = m.output(child.ty()); connect(o, child); } @@ -506,10 +506,10 @@ fn test_enums() { data_out: _, b_out: _, 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); + } = &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, @@ -528,7 +528,7 @@ fn test_enums() { ); // make sure matching on SimValue works #[hdl(sim)] - match io.b_out { + match &io.b_out { HdlNone => println!("io.b_out is HdlNone"), HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1), } @@ -706,13 +706,13 @@ fn test_memories() { w_en, w_data, w_mask, - } = 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); + } = &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, @@ -979,10 +979,10 @@ fn test_memories2() { }, ) in io_cycles.into_iter().enumerate() { - sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static()); + sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static::>()); sim.write_bool(sim.io().rw.en, en); sim.write_bool(sim.io().rw.wmode, wmode); - sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static()); + sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static::>()); sim.write_bool(sim.io().rw.wmask, wmask); sim.advance_time(SimDuration::from_nanos(250)); sim.write_clock(sim.io().rw.clk, true); @@ -1195,9 +1195,9 @@ fn test_memories3() { w_data: [0; 8], w_mask: [false; 8], }); - sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static()); + sim.write_bool_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_or_int(sim.io().w.addr, w_addr.cast_to_static::>()); sim.write_bool(sim.io().w.en, w_en); for (i, v) in w_data.into_iter().enumerate() { sim.write_bool_or_int(sim.io().w.data[i], v); @@ -1261,6 +1261,310 @@ fn test_memories3() { } } +#[hdl_module(outline_generated)] +pub fn many_memories() { + #[hdl] + let r: Array>, 8> = m.input(); + #[hdl] + let w: Array>, 8> = m.input(); + for (mem_index, (r, w)) in r.into_iter().zip(w).enumerate() { + let mut mem = memory_with_init_and_loc( + &format!("mem_{mem_index}"), + (0..16) + .map(|bit_index| mem_index.pow(5).to_expr()[bit_index]) + .collect::>(), + SourceLocation::caller(), + ); + connect_any(mem.new_read_port(), r); + connect_any(mem.new_write_port(), w); + } +} + +#[hdl] +#[test] +fn test_many_memories() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(many_memories()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + for r in sim.io().r { + sim.write_clock(r.clk, false); + } + for w in sim.io().w { + sim.write_clock(w.clk, false); + } + #[hdl(cmp_eq)] + struct IO { + r_addr: UInt<4>, + r_en: Bool, + r_data: Array, + w_addr: UInt<4>, + w_en: Bool, + w_data: Array, + w_mask: Array, + } + let io_cycles = [ + #[hdl(sim)] + IO { + r_addr: 0_hdl_u4, + r_en: false, + r_data: [false; 8], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [false; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0_hdl_u4, + r_en: true, + r_data: [false, true, false, true, false, true, false, true], + w_addr: 0_hdl_u4, + w_en: true, + w_data: [true; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0_hdl_u4, + r_en: true, + r_data: [true; 8], + w_addr: 0_hdl_u4, + w_en: true, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0_hdl_u4, + r_en: true, + r_data: [false; 8], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 1_hdl_u4, + r_en: true, + r_data: [false, false, false, true, false, false, false, true], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 2_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, true, false, true], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 3_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, false, false, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 4_hdl_u4, + r_en: true, + r_data: [false, false, false, true, false, true, false, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 5_hdl_u4, + r_en: true, + r_data: [false, false, true, true, false, true, true, true], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 6_hdl_u4, + r_en: true, + r_data: [false, false, false, true, false, false, true, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 7_hdl_u4, + r_en: true, + r_data: [false, false, false, true, false, false, false, true], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 8_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, false, false, true], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 9_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, false, true, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0xA_hdl_u4, + r_en: true, + r_data: [false, false, false, false, true, true, true, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0xB_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, true, true, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0xC_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, false, true, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0xD_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, false, false, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0xE_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, false, false, true], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + #[hdl(sim)] + IO { + r_addr: 0xF_hdl_u4, + r_en: true, + r_data: [false, false, false, false, false, false, false, false], + w_addr: 0_hdl_u4, + w_en: false, + w_data: [false; 8], + w_mask: [true; 8], + }, + ]; + for (cycle, expected) in io_cycles.into_iter().enumerate() { + #[hdl(sim)] + let IO { + r_addr, + r_en, + r_data: _, + w_addr, + w_en, + w_data, + w_mask, + } = &expected; + for (((r, w), w_data), w_mask) in sim + .io() + .r + .into_iter() + .zip(sim.io().w) + .zip(w_data.iter()) + .zip(w_mask.iter()) + { + sim.write(r.addr, r_addr); + sim.write(r.en, r_en); + sim.write(w.addr, w_addr); + sim.write(w.en, w_en); + sim.write(w.data, w_data); + sim.write(w.mask, w_mask); + } + let io = #[hdl(sim)] + IO { + r_addr, + r_en, + r_data: std::array::from_fn(|i| sim.read(sim.io().r[i].data)), + w_addr, + w_en, + w_data, + w_mask, + }; + assert_eq!( + expected, + io, + "vcd:\n{}\ncycle: {cycle}", + String::from_utf8(writer.take()).unwrap(), + ); + sim.advance_time(SimDuration::from_micros(1)); + for r in sim.io().r { + sim.write_clock(r.clk, true); + } + for w in sim.io().w { + sim.write_clock(w.clk, true); + } + sim.advance_time(SimDuration::from_micros(1)); + for r in sim.io().r { + sim.write_clock(r.clk, false); + } + for w in sim.io().w { + sim.write_clock(w.clk, false); + } + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/many_memories.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/many_memories.txt") { + panic!(); + } +} + #[hdl_module(outline_generated)] pub fn duplicate_names() { #[hdl] @@ -1722,3 +2026,472 @@ fn test_sim_only_connects() { panic!(); } } + +#[hdl_module(outline_generated, extern)] +pub fn sim_fork_join() +where + ConstUsize: KnownSize, +{ + #[hdl] + let clocks: Array = m.input(); + #[hdl] + let outputs: Array, N> = m.output(); + m.extern_module_simulation_fn((clocks, outputs), |(clocks, outputs), mut sim| async move { + sim.write(outputs, [0u8; N]).await; + loop { + sim.fork_join( + clocks + .into_iter() + .zip(outputs) + .map(|(clock, output)| { + move |mut sim: ExternModuleSimulationState| async move { + sim.wait_for_clock_edge(clock).await; + let v = sim + .read_bool_or_int(output) + .await + .to_bigint() + .try_into() + .expect("known to be in range"); + sim.write(output, 1u8.wrapping_add(v)).await; + } + }) + .collect::>(), + ) + .await; + } + }); +} + +#[test] +fn test_sim_fork_join() { + let _n = SourceLocation::normalize_files_for_tests(); + const N: usize = 3; + let mut sim = Simulation::new(sim_fork_join::()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write(sim.io().clocks, [false; N]); + let mut clocks_triggered = [false; N]; + let mut expected = [0u8; N]; + for i0 in 0..N { + for i1 in 0..N { + for i2 in 0..N { + for i3 in 0..N { + let indexes = [i0, i1, i2, i3]; + for i in indexes { + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clocks[i], true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clocks[i], false); + if !clocks_triggered[i] { + expected[i] = expected[i].wrapping_add(1); + } + clocks_triggered[i] = true; + if clocks_triggered == [true; N] { + clocks_triggered = [false; N]; + } + let output = sim.read(sim.io().outputs); + assert_eq!(output, expected.to_sim_value(), "indexes={indexes:?} i={i}"); + } + } + } + } + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/sim_fork_join.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_fork_join.txt") { + panic!(); + } +} + +#[hdl_module(outline_generated, extern)] +pub fn sim_fork_join_scope() +where + ConstUsize: KnownSize, +{ + #[hdl] + let clocks: Array = m.input(); + #[hdl] + let outputs: Array, N> = m.output(); + m.extern_module_simulation_fn((clocks, outputs), |(clocks, outputs), mut sim| async move { + sim.write(outputs, [0u8; N]).await; + loop { + let written = vec![std::cell::Cell::new(false); N]; // test shared scope + let written = &written; // work around move in async move + sim.fork_join_scope(|scope, _| async move { + let mut spawned = vec![]; + for i in 0..N { + let join_handle = + scope.spawn(move |_, mut sim: ExternModuleSimulationState| async move { + sim.wait_for_clock_edge(clocks[i]).await; + let v = sim + .read_bool_or_int(outputs[i]) + .await + .to_bigint() + .try_into() + .expect("known to be in range"); + sim.write(outputs[i], 1u8.wrapping_add(v)).await; + written[i].set(true); + i + }); + if i % 2 == 0 && i < N - 1 { + spawned.push((i, join_handle)); + } + } + for (i, join_handle) in spawned { + assert_eq!(i, join_handle.join().await); + } + }) + .await; + for written in written { + assert!(written.get()); + } + } + }); +} + +#[test] +fn test_sim_fork_join_scope() { + let _n = SourceLocation::normalize_files_for_tests(); + const N: usize = 3; + let mut sim = Simulation::new(sim_fork_join_scope::()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write(sim.io().clocks, [false; N]); + let mut clocks_triggered = [false; N]; + let mut expected = [0u8; N]; + for i0 in 0..N { + for i1 in 0..N { + for i2 in 0..N { + for i3 in 0..N { + let indexes = [i0, i1, i2, i3]; + for i in indexes { + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clocks[i], true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clocks[i], false); + if !clocks_triggered[i] { + expected[i] = expected[i].wrapping_add(1); + } + clocks_triggered[i] = true; + if clocks_triggered == [true; N] { + clocks_triggered = [false; N]; + } + let output = sim.read(sim.io().outputs); + assert_eq!(output, expected.to_sim_value(), "indexes={indexes:?} i={i}"); + } + } + } + } + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/sim_fork_join_scope.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_fork_join_scope.txt") { + panic!(); + } +} + +#[hdl_module(outline_generated, extern)] +pub fn sim_resettable_counter() { + #[hdl] + let cd: ClockDomain = m.input(); + #[hdl] + let out: UInt<8> = m.output(); + m.extern_module_simulation_fn((cd, out), |(cd, out), mut sim| async move { + sim.resettable( + cd, + |mut sim: ExternModuleSimulationState| async move { + sim.write(out, 0u8).await; + }, + |mut sim: ExternModuleSimulationState, ()| async move { + loop { + sim.wait_for_clock_edge(cd.clk).await; + let v: u8 = sim + .read(out) + .await + .to_bigint() + .try_into() + .expect("known to be in range"); + sim.write(out, v.wrapping_add(1)).await; + } + }, + ) + .await + }); +} + +fn test_sim_resettable_counter_helper( + sim: &mut Simulation>, + immediate_reset: bool, +) { + sim.write_clock(sim.io().cd.clk, false); + sim.write_reset(sim.io().cd.rst, immediate_reset); + for _ in 0..2 { + sim.advance_time(SimDuration::from_micros(1)); + sim.write_clock(sim.io().cd.clk, true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write_clock(sim.io().cd.clk, false); + sim.write_reset(sim.io().cd.rst, true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write_clock(sim.io().cd.clk, true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write_clock(sim.io().cd.clk, false); + sim.write_reset(sim.io().cd.rst, false); + for expected in 0..3u8 { + assert_eq!(sim.read(sim.io().out), expected.to_sim_value()); + sim.advance_time(SimDuration::from_micros(1)); + sim.write_clock(sim.io().cd.clk, true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write_clock(sim.io().cd.clk, false); + } + } +} + +#[test] +fn test_sim_resettable_counter_sync() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(sim_resettable_counter::()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + test_sim_resettable_counter_helper(&mut sim, 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_resettable_counter_sync.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_resettable_counter_sync.txt") { + panic!(); + } +} + +#[test] +fn test_sim_resettable_counter_sync_immediate_reset() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(sim_resettable_counter::()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + test_sim_resettable_counter_helper(&mut sim, true); + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/sim_resettable_counter_sync_immediate_reset.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_resettable_counter_sync_immediate_reset.txt") { + panic!(); + } +} + +#[test] +fn test_sim_resettable_counter_async() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(sim_resettable_counter::()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + test_sim_resettable_counter_helper(&mut sim, 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_resettable_counter_async.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_resettable_counter_async.txt") { + panic!(); + } +} + +#[test] +fn test_sim_resettable_counter_async_immediate_reset() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(sim_resettable_counter::()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + test_sim_resettable_counter_helper(&mut sim, true); + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/sim_resettable_counter_async_immediate_reset.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_resettable_counter_async_immediate_reset.txt") { + panic!(); + } +} + +#[hdl_module(outline_generated)] +pub fn phantom_const() { + #[hdl] + let out: Array>, 2> = + m.output(Array::new_static(PhantomConst::new_sized(vec![ + "a".into(), + "b".into(), + ]))); + let _ = out; + #[hdl] + let mut mem = memory(PhantomConst::new("mem_element")); + mem.depth(1); + let port = mem.new_read_port(); + connect_any(port.addr, 0u8); + connect(port.clk, false.to_clock()); + connect(port.en, false); +} + +#[test] +fn test_phantom_const() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(phantom_const()); + 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/phantom_const.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/phantom_const.txt") { + panic!(); + } +} + +#[hdl_module(outline_generated, extern)] +pub fn sim_read_past() +where + ConstUsize: KnownSize, +{ + #[hdl] + let clocks: Array = m.input(); + #[hdl] + let outputs: Array, N> = m.output(); + #[hdl] + let past_clocks: Array = m.output(); + #[hdl] + let past_outputs: Array, N> = m.output(); + for clock in clocks { + m.register_clock_for_past(clock); + } + m.extern_module_simulation_fn( + (clocks, outputs, past_clocks, past_outputs), + |(clocks, outputs, past_clocks, past_outputs), mut sim| async move { + sim.write(outputs, [0u8; N]).await; + sim.write(past_clocks, [false; N]).await; + sim.write(past_outputs, [0u8; N]).await; + loop { + sim.fork_join_scope(|scope, _| async move { + for (clock, output) in clocks.into_iter().zip(outputs) { + scope.spawn_detached( + move |_, mut sim: ExternModuleSimulationState| async move { + sim.wait_for_clock_edge(clock).await; + dbg!(clock); + let v = sim + .read_bool_or_int(output) + .await + .to_bigint() + .try_into() + .expect("known to be in range"); + sim.write(output, 1u8.wrapping_add(v)).await; + let past_outputs_v = sim.read_past(outputs, clock).await; + dbg!(&past_outputs_v); + sim.write(past_outputs, past_outputs_v).await; + let past_clocks_v = sim.read_past(clocks, clock).await; + dbg!(&past_clocks_v); + sim.write(past_clocks, past_clocks_v).await; + }, + ); + } + }) + .await; + } + }, + ); +} + +#[test] +fn test_sim_read_past() { + let _n = SourceLocation::normalize_files_for_tests(); + const N: usize = 3; + let mut sim = Simulation::new(sim_read_past::()); + // sim.set_breakpoints_unstable(Default::default(), true); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write(sim.io().clocks, [false; N]); + let mut clocks_triggered = [false; N]; + let mut expected = [0u8; N]; + let mut past_clocks_expected = [false; N]; + let mut past_expected = expected; + for i0 in 0..N { + for i1 in 0..N { + for i2 in 0..N { + for i3 in 0..N { + let indexes = [i0, i1, i2, i3]; + for i in indexes { + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clocks[i], true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clocks[i], false); + if !clocks_triggered[i] { + past_expected = expected; + expected[i] = expected[i].wrapping_add(1); + past_clocks_expected = [false; N]; + past_clocks_expected[i] = true; + } + dbg!(past_expected); + clocks_triggered[i] = true; + if clocks_triggered == [true; N] { + clocks_triggered = [false; N]; + } + let output = sim.read(sim.io().outputs); + assert_eq!(output, expected.to_sim_value(), "indexes={indexes:?} i={i}"); + let past_clocks = sim.read(sim.io().past_clocks); + assert_eq!( + past_clocks, + past_clocks_expected + .to_sim_value_with_type(Array::::default()), + "indexes={indexes:?} i={i}" + ); + let past_outputs = sim.read(sim.io().past_outputs); + dbg!(&past_outputs); + assert_eq!( + past_outputs, + past_expected.to_sim_value(), + "indexes={indexes:?} i={i}" + ); + } + } + } + } + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/sim_read_past.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_read_past.txt") { + panic!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/array_rw.txt b/crates/fayalite/tests/sim/expected/array_rw.txt index 12e86f30..27b040d2 100644 --- a/crates/fayalite/tests/sim/expected/array_rw.txt +++ b/crates/fayalite/tests/sim/expected/array_rw.txt @@ -826,9 +826,9 @@ Simulation { }.write_index, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "array_rw", children: [ @@ -1699,7 +1699,12 @@ Simulation { }, ), ], - instant: 34 μs, clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 34 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt b/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt index 58b2d20f..d4707926 100644 --- a/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt +++ b/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt @@ -122,9 +122,9 @@ Simulation { }.i, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "conditional_assignment_last", children: [ @@ -177,7 +177,12 @@ Simulation { }, ), ], - instant: 2 μs, clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 2 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/connect_const.txt b/crates/fayalite/tests/sim/expected/connect_const.txt index 182ed84f..56ea4ad7 100644 --- a/crates/fayalite/tests/sim/expected/connect_const.txt +++ b/crates/fayalite/tests/sim/expected/connect_const.txt @@ -98,9 +98,9 @@ Simulation { }.o, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "connect_const", children: [ @@ -130,7 +130,12 @@ Simulation { ], trace_memories: {}, trace_writers: [], - instant: 0 s, clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 0 s, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/connect_const_reset.txt b/crates/fayalite/tests/sim/expected/connect_const_reset.txt index f56a6b4e..6b5814a7 100644 --- a/crates/fayalite/tests/sim/expected/connect_const_reset.txt +++ b/crates/fayalite/tests/sim/expected/connect_const_reset.txt @@ -21,7 +21,7 @@ Simulation { }, SlotDebugData { name: "", - ty: Bool, + ty: UInt<1>, }, SlotDebugData { name: "", @@ -51,12 +51,12 @@ Simulation { insns: [ // at: module-XXXXXXXXXX.rs:1:1 0: Const { - dest: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, value: 0x1, }, 1: Copy { dest: StatePartIndex(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset }, - src: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, // at: module-XXXXXXXXXX.rs:4:1 2: Copy { @@ -141,9 +141,9 @@ Simulation { }.reset_out, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "connect_const_reset", children: [ @@ -197,7 +197,12 @@ Simulation { }, ), ], - instant: 1 μs, clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 1 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/counter_async.txt b/crates/fayalite/tests/sim/expected/counter_async.txt index 8c8809ab..86bde889 100644 --- a/crates/fayalite/tests/sim/expected/counter_async.txt +++ b/crates/fayalite/tests/sim/expected/counter_async.txt @@ -100,51 +100,51 @@ Simulation { dest: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.rst", ty: AsyncReset }, }, + 3: IsNonZeroDestIsSmall { + dest: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.clk", ty: Clock }, + }, + 4: 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 - 3: Const { + 5: Const { dest: StatePartIndex(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, value: 0x3, }, // at: module-XXXXXXXXXX.rs:3:1 - 4: BranchIfZero { - target: 6, + 6: BranchIfZero { + target: 8, value: StatePartIndex(6), // (0x0) SlotDebugData { name: "", ty: Bool }, }, - 5: Copy { + 7: Copy { dest: StatePartIndex(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> }, src: StatePartIndex(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, }, // at: module-XXXXXXXXXX.rs:1:1 - 6: Add { + 8: Add { dest: StatePartIndex(8), // (0x4) SlotDebugData { name: "", ty: UInt<5> }, lhs: StatePartIndex(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> }, rhs: StatePartIndex(7), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, - 7: CastToUInt { + 9: CastToUInt { dest: StatePartIndex(9), // (0x4) SlotDebugData { name: "", ty: UInt<4> }, src: StatePartIndex(8), // (0x4) SlotDebugData { name: "", ty: UInt<5> }, dest_width: 4, }, // at: module-XXXXXXXXXX.rs:4:1 - 8: Copy { + 10: Copy { dest: StatePartIndex(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> }, src: StatePartIndex(9), // (0x4) SlotDebugData { name: "", ty: UInt<4> }, }, // at: module-XXXXXXXXXX.rs:6:1 - 9: Copy { + 11: Copy { dest: StatePartIndex(2), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count", ty: UInt<4> }, src: StatePartIndex(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> }, }, // at: module-XXXXXXXXXX.rs:3:1 - 10: IsNonZeroDestIsSmall { - dest: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, - src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.clk", ty: Clock }, - }, - 11: 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 }, - }, 12: BranchIfSmallNonZero { target: 16, value: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, @@ -261,9 +261,9 @@ Simulation { }.count, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "counter", children: [ @@ -329,7 +329,7 @@ Simulation { index: StatePartIndex(0), }, state: 0x1, - last_state: 0x1, + last_state: 0x0, }, SimTrace { id: TraceScalarId(1), @@ -355,7 +355,7 @@ Simulation { ty: UInt<4>, }, state: 0x3, - last_state: 0x3, + last_state: 0x2, }, ], trace_memories: {}, @@ -368,9 +368,14 @@ Simulation { }, ), ], - instant: 66 μs, clocks_triggered: [ StatePartIndex(1), ], + event_queue: EventQueue(EventQueueData { + instant: 66 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/counter_async.vcd b/crates/fayalite/tests/sim/expected/counter_async.vcd index a4b2ee91..dab690f5 100644 --- a/crates/fayalite/tests/sim/expected/counter_async.vcd +++ b/crates/fayalite/tests/sim/expected/counter_async.vcd @@ -26,192 +26,192 @@ b11 $ 0! #3000000 1! -b100 $ b100 # +b100 $ #4000000 0! #5000000 1! -b101 $ b101 # +b101 $ #6000000 0! #7000000 1! -b110 $ b110 # +b110 $ #8000000 0! #9000000 1! -b111 $ b111 # +b111 $ #10000000 0! #11000000 1! -b1000 $ b1000 # +b1000 $ #12000000 0! #13000000 1! -b1001 $ b1001 # +b1001 $ #14000000 0! #15000000 1! -b1010 $ b1010 # +b1010 $ #16000000 0! #17000000 1! -b1011 $ b1011 # +b1011 $ #18000000 0! #19000000 1! -b1100 $ b1100 # +b1100 $ #20000000 0! #21000000 1! -b1101 $ b1101 # +b1101 $ #22000000 0! #23000000 1! -b1110 $ b1110 # +b1110 $ #24000000 0! #25000000 1! -b1111 $ b1111 # +b1111 $ #26000000 0! #27000000 1! -b0 $ b0 # +b0 $ #28000000 0! #29000000 1! -b1 $ b1 # +b1 $ #30000000 0! #31000000 1! -b10 $ b10 # +b10 $ #32000000 0! #33000000 1! -b11 $ b11 # +b11 $ #34000000 0! #35000000 1! -b100 $ b100 # +b100 $ #36000000 0! #37000000 1! -b101 $ b101 # +b101 $ #38000000 0! #39000000 1! -b110 $ b110 # +b110 $ #40000000 0! #41000000 1! -b111 $ b111 # +b111 $ #42000000 0! #43000000 1! -b1000 $ b1000 # +b1000 $ #44000000 0! #45000000 1! -b1001 $ b1001 # +b1001 $ #46000000 0! #47000000 1! -b1010 $ b1010 # +b1010 $ #48000000 0! #49000000 1! -b1011 $ b1011 # +b1011 $ #50000000 0! #51000000 1! -b1100 $ b1100 # +b1100 $ #52000000 0! #53000000 1! -b1101 $ b1101 # +b1101 $ #54000000 0! #55000000 1! -b1110 $ b1110 # +b1110 $ #56000000 0! #57000000 1! -b1111 $ b1111 # +b1111 $ #58000000 0! #59000000 1! -b0 $ b0 # +b0 $ #60000000 0! #61000000 1! -b1 $ b1 # +b1 $ #62000000 0! #63000000 1! -b10 $ b10 # +b10 $ #64000000 0! #65000000 1! -b11 $ b11 # +b11 $ #66000000 diff --git a/crates/fayalite/tests/sim/expected/counter_sync.txt b/crates/fayalite/tests/sim/expected/counter_sync.txt index 1d975b3d..0a7517e9 100644 --- a/crates/fayalite/tests/sim/expected/counter_sync.txt +++ b/crates/fayalite/tests/sim/expected/counter_sync.txt @@ -112,21 +112,21 @@ Simulation { dest: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.rst", ty: SyncReset }, }, - // at: module-XXXXXXXXXX.rs:1:1 - 6: Const { - dest: StatePartIndex(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, - value: 0x3, - }, - // at: module-XXXXXXXXXX.rs:3:1 - 7: IsNonZeroDestIsSmall { + 6: IsNonZeroDestIsSmall { dest: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.clk", ty: Clock }, }, - 8: AndSmall { + 7: 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 + 8: Const { + dest: StatePartIndex(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, + value: 0x3, + }, + // at: module-XXXXXXXXXX.rs:3:1 9: BranchIfSmallZero { target: 14, value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, @@ -242,9 +242,9 @@ Simulation { }.count, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "counter", children: [ @@ -310,7 +310,7 @@ Simulation { index: StatePartIndex(0), }, state: 0x1, - last_state: 0x1, + last_state: 0x0, }, SimTrace { id: TraceScalarId(1), @@ -336,7 +336,7 @@ Simulation { ty: UInt<4>, }, state: 0x3, - last_state: 0x3, + last_state: 0x2, }, ], trace_memories: {}, @@ -349,9 +349,14 @@ Simulation { }, ), ], - instant: 66 μs, clocks_triggered: [ StatePartIndex(1), ], + event_queue: EventQueue(EventQueueData { + instant: 66 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/counter_sync.vcd b/crates/fayalite/tests/sim/expected/counter_sync.vcd index bf6249e4..9504a30a 100644 --- a/crates/fayalite/tests/sim/expected/counter_sync.vcd +++ b/crates/fayalite/tests/sim/expected/counter_sync.vcd @@ -16,199 +16,199 @@ b0 $ $end #1000000 1! -b11 $ b11 # +b11 $ 0" #2000000 0! #3000000 1! -b100 $ b100 # +b100 $ #4000000 0! #5000000 1! -b101 $ b101 # +b101 $ #6000000 0! #7000000 1! -b110 $ b110 # +b110 $ #8000000 0! #9000000 1! -b111 $ b111 # +b111 $ #10000000 0! #11000000 1! -b1000 $ b1000 # +b1000 $ #12000000 0! #13000000 1! -b1001 $ b1001 # +b1001 $ #14000000 0! #15000000 1! -b1010 $ b1010 # +b1010 $ #16000000 0! #17000000 1! -b1011 $ b1011 # +b1011 $ #18000000 0! #19000000 1! -b1100 $ b1100 # +b1100 $ #20000000 0! #21000000 1! -b1101 $ b1101 # +b1101 $ #22000000 0! #23000000 1! -b1110 $ b1110 # +b1110 $ #24000000 0! #25000000 1! -b1111 $ b1111 # +b1111 $ #26000000 0! #27000000 1! -b0 $ b0 # +b0 $ #28000000 0! #29000000 1! -b1 $ b1 # +b1 $ #30000000 0! #31000000 1! -b10 $ b10 # +b10 $ #32000000 0! #33000000 1! -b11 $ b11 # +b11 $ #34000000 0! #35000000 1! -b100 $ b100 # +b100 $ #36000000 0! #37000000 1! -b101 $ b101 # +b101 $ #38000000 0! #39000000 1! -b110 $ b110 # +b110 $ #40000000 0! #41000000 1! -b111 $ b111 # +b111 $ #42000000 0! #43000000 1! -b1000 $ b1000 # +b1000 $ #44000000 0! #45000000 1! -b1001 $ b1001 # +b1001 $ #46000000 0! #47000000 1! -b1010 $ b1010 # +b1010 $ #48000000 0! #49000000 1! -b1011 $ b1011 # +b1011 $ #50000000 0! #51000000 1! -b1100 $ b1100 # +b1100 $ #52000000 0! #53000000 1! -b1101 $ b1101 # +b1101 $ #54000000 0! #55000000 1! -b1110 $ b1110 # +b1110 $ #56000000 0! #57000000 1! -b1111 $ b1111 # +b1111 $ #58000000 0! #59000000 1! -b0 $ b0 # +b0 $ #60000000 0! #61000000 1! -b1 $ b1 # +b1 $ #62000000 0! #63000000 1! -b10 $ b10 # +b10 $ #64000000 0! #65000000 1! -b11 $ b11 # +b11 $ #66000000 diff --git a/crates/fayalite/tests/sim/expected/duplicate_names.txt b/crates/fayalite/tests/sim/expected/duplicate_names.txt index 4c54aa85..64bbbe63 100644 --- a/crates/fayalite/tests/sim/expected/duplicate_names.txt +++ b/crates/fayalite/tests/sim/expected/duplicate_names.txt @@ -102,9 +102,9 @@ Simulation { uninitialized_ios: {}, io_targets: {}, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "duplicate_names", children: [ @@ -160,7 +160,12 @@ Simulation { }, ), ], - instant: 1 μs, clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 1 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/enums.txt b/crates/fayalite/tests/sim/expected/enums.txt index 4850a215..a193e926 100644 --- a/crates/fayalite/tests/sim/expected/enums.txt +++ b/crates/fayalite/tests/sim/expected/enums.txt @@ -1003,65 +1003,64 @@ Simulation { dest: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.rst", ty: SyncReset }, }, + 97: IsNonZeroDestIsSmall { + dest: StatePartIndex(4), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.clk", ty: Clock }, + }, + 98: AndSmall { + 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:1:1 - 97: Const { + 99: Const { dest: StatePartIndex(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, value: 0x0, }, - 98: Copy { + 100: Copy { 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:12:1 - 99: BranchIfZero { - target: 107, + 101: BranchIfZero { + target: 109, value: StatePartIndex(2), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::en", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:13:1 - 100: BranchIfZero { - target: 102, + 102: BranchIfZero { + target: 104, value: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:14:1 - 101: Copy { + 103: 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 - 102: BranchIfNonZero { - target: 107, + 104: BranchIfNonZero { + target: 109, value: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:15:1 - 103: BranchIfZero { - target: 105, + 105: BranchIfZero { + target: 107, value: StatePartIndex(48), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:16:1 - 104: Copy { + 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(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, + 107: BranchIfNonZero { + target: 109, value: StatePartIndex(48), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:17:1 - 106: Copy { + 108: 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(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(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})} }, @@ -1454,9 +1453,9 @@ Simulation { }.which_out, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "enums", children: [ @@ -1744,7 +1743,7 @@ Simulation { index: StatePartIndex(0), }, state: 0x1, - last_state: 0x1, + last_state: 0x0, }, SimTrace { id: TraceScalarId(1), @@ -1924,9 +1923,14 @@ Simulation { }, ), ], - instant: 16 μs, clocks_triggered: [ StatePartIndex(3), ], + event_queue: EventQueue(EventQueueData { + instant: 16 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/extern_module.txt b/crates/fayalite/tests/sim/expected/extern_module.txt index e09a7671..f49106f6 100644 --- a/crates/fayalite/tests/sim/expected/extern_module.txt +++ b/crates/fayalite/tests/sim/expected/extern_module.txt @@ -102,6 +102,7 @@ Simulation { }.o, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [ SimulationExternModuleState { @@ -136,6 +137,7 @@ Simulation { }, }, did_initial_settle: true, + clocks_for_past: {}, }, sim: ExternModuleSimulation { generator: SimGeneratorFn { @@ -186,14 +188,8 @@ Simulation { running_generator: Some( ..., ), - wait_targets: { - Instant( - 20.500000000000 μs, - ), - }, }, ], - state_ready_to_run: false, trace_decls: TraceModule { name: "extern_module", children: [ @@ -234,7 +230,7 @@ Simulation { index: StatePartIndex(1), }, state: 0x1, - last_state: 0x1, + last_state: 0x0, }, ], trace_memories: {}, @@ -247,7 +243,21 @@ Simulation { }, ), ], - instant: 20 μs, clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 20 μs, + events: { + Event { + instant: 20.500000000000 μs, + kind: ExternModule( + 0, + ), + }: Wakers( + 1, + ), + }, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ 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 index e026a50c..5d6a0bc9 100644 --- a/crates/fayalite/tests/sim/expected/extern_module.vcd +++ b/crates/fayalite/tests/sim/expected/extern_module.vcd @@ -6,8 +6,9 @@ $upscope $end $enddefinitions $end $dumpvars 0! -1" +0" $end +1" #500000 #1500000 0" diff --git a/crates/fayalite/tests/sim/expected/extern_module2.txt b/crates/fayalite/tests/sim/expected/extern_module2.txt index 1023b2ba..fa6e767e 100644 --- a/crates/fayalite/tests/sim/expected/extern_module2.txt +++ b/crates/fayalite/tests/sim/expected/extern_module2.txt @@ -121,6 +121,7 @@ Simulation { }.o, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [ SimulationExternModuleState { @@ -167,6 +168,7 @@ Simulation { }, }, did_initial_settle: true, + clocks_for_past: {}, }, sim: ExternModuleSimulation { generator: SimGeneratorFn { @@ -234,55 +236,8 @@ Simulation { 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: [ @@ -356,7 +311,113 @@ Simulation { }, ), ], - instant: 60 μs, clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 60 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 59, + values: { + 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, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + 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, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 59, + .. + }, + }, + ), + }, .. } \ 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 index 464f4bd4..42045679 100644 --- a/crates/fayalite/tests/sim/expected/extern_module2.vcd +++ b/crates/fayalite/tests/sim/expected/extern_module2.vcd @@ -8,8 +8,9 @@ $enddefinitions $end $dumpvars 1! 0" -b1001000 # +b0 # $end +b1001000 # #1000000 1" b1100101 # diff --git a/crates/fayalite/tests/sim/expected/many_memories.txt b/crates/fayalite/tests/sim/expected/many_memories.txt new file mode 100644 index 00000000..c521d725 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/many_memories.txt @@ -0,0 +1,7787 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 96, + debug_data: [ + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + ], + .. + }, + big_slots: StatePartLayout { + len: 160, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[0].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[0].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[0].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[0].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[1].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[1].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[1].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[1].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[2].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[2].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[2].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[2].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[3].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[3].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[3].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[3].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[4].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[4].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[4].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[4].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[5].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[5].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[5].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[5].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[6].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[6].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[6].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[6].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[7].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[7].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[7].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::r[7].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].mask", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].mask", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].mask", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].mask", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].mask", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].mask", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].mask", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].mask", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.mask", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.mask", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.mask", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.mask", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.mask", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.mask", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.mask", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.addr", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.data", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.mask", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 8, + debug_data: [ + (), + (), + (), + (), + (), + (), + (), + (), + ], + layout_data: [ + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x0, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x1, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x0, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x1, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x1, + [0x1]: 0x1, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x1, + [0x5]: 0x1, + [0x6]: 0x1, + [0x7]: 0x1, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x0, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x1, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x1, + [0x1]: 0x0, + [0x2]: 0x1, + [0x3]: 0x0, + [0x4]: 0x1, + [0x5]: 0x1, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x1, + [0xb]: 0x1, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x1, + [0x6]: 0x1, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x1, + [0xa]: 0x1, + [0xb]: 0x1, + [0xc]: 0x1, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x1, + [0x1]: 0x1, + [0x2]: 0x1, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x1, + [0x6]: 0x0, + [0x7]: 0x1, + [0x8]: 0x1, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x1, + [0xf]: 0x0, + ], + }, + ], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:8:1 + 0: Copy { + dest: StatePartIndex(153), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.addr", ty: UInt<4> }, + src: StatePartIndex(67), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].addr", ty: UInt<4> }, + }, + 1: Copy { + dest: StatePartIndex(154), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.en", ty: Bool }, + src: StatePartIndex(68), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].en", ty: Bool }, + }, + 2: Copy { + dest: StatePartIndex(155), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.clk", ty: Clock }, + src: StatePartIndex(69), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].clk", ty: Clock }, + }, + 3: Copy { + dest: StatePartIndex(156), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.data", ty: Bool }, + src: StatePartIndex(70), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].data", ty: Bool }, + }, + 4: Copy { + dest: StatePartIndex(157), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.mask", ty: Bool }, + src: StatePartIndex(71), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[7].mask", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 5: Copy { + dest: StatePartIndex(151), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.clk", ty: Clock }, + src: StatePartIndex(30), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[7].clk", ty: Clock }, + }, + 6: Copy { + dest: StatePartIndex(150), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.en", ty: Bool }, + src: StatePartIndex(29), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[7].en", ty: Bool }, + }, + 7: Copy { + dest: StatePartIndex(149), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.addr", ty: UInt<4> }, + src: StatePartIndex(28), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[7].addr", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 8: Copy { + dest: StatePartIndex(142), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.addr", ty: UInt<4> }, + src: StatePartIndex(62), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].addr", ty: UInt<4> }, + }, + 9: Copy { + dest: StatePartIndex(143), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.en", ty: Bool }, + src: StatePartIndex(63), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].en", ty: Bool }, + }, + 10: Copy { + dest: StatePartIndex(144), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.clk", ty: Clock }, + src: StatePartIndex(64), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].clk", ty: Clock }, + }, + 11: Copy { + dest: StatePartIndex(145), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.data", ty: Bool }, + src: StatePartIndex(65), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].data", ty: Bool }, + }, + 12: Copy { + dest: StatePartIndex(146), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.mask", ty: Bool }, + src: StatePartIndex(66), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[6].mask", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 13: Copy { + dest: StatePartIndex(140), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.clk", ty: Clock }, + src: StatePartIndex(26), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[6].clk", ty: Clock }, + }, + 14: Copy { + dest: StatePartIndex(139), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.en", ty: Bool }, + src: StatePartIndex(25), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[6].en", ty: Bool }, + }, + 15: Copy { + dest: StatePartIndex(138), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.addr", ty: UInt<4> }, + src: StatePartIndex(24), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[6].addr", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 16: Copy { + dest: StatePartIndex(131), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.addr", ty: UInt<4> }, + src: StatePartIndex(57), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].addr", ty: UInt<4> }, + }, + 17: Copy { + dest: StatePartIndex(132), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.en", ty: Bool }, + src: StatePartIndex(58), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].en", ty: Bool }, + }, + 18: Copy { + dest: StatePartIndex(133), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.clk", ty: Clock }, + src: StatePartIndex(59), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].clk", ty: Clock }, + }, + 19: Copy { + dest: StatePartIndex(134), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.data", ty: Bool }, + src: StatePartIndex(60), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].data", ty: Bool }, + }, + 20: Copy { + dest: StatePartIndex(135), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.mask", ty: Bool }, + src: StatePartIndex(61), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[5].mask", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 21: Copy { + dest: StatePartIndex(129), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.clk", ty: Clock }, + src: StatePartIndex(22), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[5].clk", ty: Clock }, + }, + 22: Copy { + dest: StatePartIndex(128), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.en", ty: Bool }, + src: StatePartIndex(21), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[5].en", ty: Bool }, + }, + 23: Copy { + dest: StatePartIndex(127), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.addr", ty: UInt<4> }, + src: StatePartIndex(20), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[5].addr", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 24: Copy { + dest: StatePartIndex(120), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.addr", ty: UInt<4> }, + src: StatePartIndex(52), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].addr", ty: UInt<4> }, + }, + 25: Copy { + dest: StatePartIndex(121), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.en", ty: Bool }, + src: StatePartIndex(53), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].en", ty: Bool }, + }, + 26: Copy { + dest: StatePartIndex(122), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.clk", ty: Clock }, + src: StatePartIndex(54), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].clk", ty: Clock }, + }, + 27: Copy { + dest: StatePartIndex(123), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.data", ty: Bool }, + src: StatePartIndex(55), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].data", ty: Bool }, + }, + 28: Copy { + dest: StatePartIndex(124), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.mask", ty: Bool }, + src: StatePartIndex(56), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[4].mask", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 29: Copy { + dest: StatePartIndex(118), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.clk", ty: Clock }, + src: StatePartIndex(18), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[4].clk", ty: Clock }, + }, + 30: Copy { + dest: StatePartIndex(117), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.en", ty: Bool }, + src: StatePartIndex(17), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[4].en", ty: Bool }, + }, + 31: Copy { + dest: StatePartIndex(116), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.addr", ty: UInt<4> }, + src: StatePartIndex(16), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[4].addr", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 32: Copy { + dest: StatePartIndex(109), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.addr", ty: UInt<4> }, + src: StatePartIndex(47), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].addr", ty: UInt<4> }, + }, + 33: Copy { + dest: StatePartIndex(110), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.en", ty: Bool }, + src: StatePartIndex(48), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].en", ty: Bool }, + }, + 34: Copy { + dest: StatePartIndex(111), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.clk", ty: Clock }, + src: StatePartIndex(49), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].clk", ty: Clock }, + }, + 35: Copy { + dest: StatePartIndex(112), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.data", ty: Bool }, + src: StatePartIndex(50), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].data", ty: Bool }, + }, + 36: Copy { + dest: StatePartIndex(113), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.mask", ty: Bool }, + src: StatePartIndex(51), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[3].mask", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 37: Copy { + dest: StatePartIndex(107), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.clk", ty: Clock }, + src: StatePartIndex(14), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[3].clk", ty: Clock }, + }, + 38: Copy { + dest: StatePartIndex(106), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.en", ty: Bool }, + src: StatePartIndex(13), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[3].en", ty: Bool }, + }, + 39: Copy { + dest: StatePartIndex(105), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.addr", ty: UInt<4> }, + src: StatePartIndex(12), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[3].addr", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 40: Copy { + dest: StatePartIndex(98), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.addr", ty: UInt<4> }, + src: StatePartIndex(42), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].addr", ty: UInt<4> }, + }, + 41: Copy { + dest: StatePartIndex(99), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.en", ty: Bool }, + src: StatePartIndex(43), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].en", ty: Bool }, + }, + 42: Copy { + dest: StatePartIndex(100), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.clk", ty: Clock }, + src: StatePartIndex(44), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].clk", ty: Clock }, + }, + 43: Copy { + dest: StatePartIndex(101), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.data", ty: Bool }, + src: StatePartIndex(45), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].data", ty: Bool }, + }, + 44: Copy { + dest: StatePartIndex(102), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.mask", ty: Bool }, + src: StatePartIndex(46), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[2].mask", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 45: Copy { + dest: StatePartIndex(96), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.clk", ty: Clock }, + src: StatePartIndex(10), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[2].clk", ty: Clock }, + }, + 46: Copy { + dest: StatePartIndex(95), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.en", ty: Bool }, + src: StatePartIndex(9), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[2].en", ty: Bool }, + }, + 47: Copy { + dest: StatePartIndex(94), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.addr", ty: UInt<4> }, + src: StatePartIndex(8), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[2].addr", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 48: Copy { + dest: StatePartIndex(87), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.addr", ty: UInt<4> }, + src: StatePartIndex(37), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].addr", ty: UInt<4> }, + }, + 49: Copy { + dest: StatePartIndex(88), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.en", ty: Bool }, + src: StatePartIndex(38), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].en", ty: Bool }, + }, + 50: Copy { + dest: StatePartIndex(89), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.clk", ty: Clock }, + src: StatePartIndex(39), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].clk", ty: Clock }, + }, + 51: Copy { + dest: StatePartIndex(90), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.data", ty: Bool }, + src: StatePartIndex(40), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].data", ty: Bool }, + }, + 52: Copy { + dest: StatePartIndex(91), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.mask", ty: Bool }, + src: StatePartIndex(41), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[1].mask", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 53: Copy { + dest: StatePartIndex(85), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.clk", ty: Clock }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[1].clk", ty: Clock }, + }, + 54: Copy { + dest: StatePartIndex(84), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.en", ty: Bool }, + src: StatePartIndex(5), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[1].en", ty: Bool }, + }, + 55: Copy { + dest: StatePartIndex(83), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.addr", ty: UInt<4> }, + src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[1].addr", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 56: Copy { + dest: StatePartIndex(76), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.addr", ty: UInt<4> }, + src: StatePartIndex(32), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].addr", ty: UInt<4> }, + }, + 57: Copy { + dest: StatePartIndex(77), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.en", ty: Bool }, + src: StatePartIndex(33), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].en", ty: Bool }, + }, + 58: Copy { + dest: StatePartIndex(78), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.clk", ty: Clock }, + src: StatePartIndex(34), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].clk", ty: Clock }, + }, + 59: Copy { + dest: StatePartIndex(79), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.data", ty: Bool }, + src: StatePartIndex(35), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].data", ty: Bool }, + }, + 60: Copy { + dest: StatePartIndex(80), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.mask", ty: Bool }, + src: StatePartIndex(36), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::w[0].mask", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 61: Copy { + dest: StatePartIndex(74), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.clk", ty: Clock }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[0].clk", ty: Clock }, + }, + 62: Copy { + dest: StatePartIndex(73), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.en", ty: Bool }, + src: StatePartIndex(1), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[0].en", ty: Bool }, + }, + 63: Copy { + dest: StatePartIndex(72), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.addr", ty: UInt<4> }, + src: StatePartIndex(0), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[0].addr", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 64: CastBigToArrayIndex { + dest: StatePartIndex(93), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(153), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.addr", ty: UInt<4> }, + }, + 65: IsNonZeroDestIsSmall { + dest: StatePartIndex(92), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(154), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.en", ty: Bool }, + }, + 66: IsNonZeroDestIsSmall { + dest: StatePartIndex(91), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(155), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.clk", ty: Clock }, + }, + 67: AndSmall { + dest: StatePartIndex(90), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(91), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(89), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 68: CastBigToArrayIndex { + dest: StatePartIndex(88), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(149), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.addr", ty: UInt<4> }, + }, + 69: IsNonZeroDestIsSmall { + dest: StatePartIndex(87), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(150), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.en", ty: Bool }, + }, + 70: BranchIfSmallZero { + target: 73, + value: StatePartIndex(87), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 71: MemoryReadUInt { + dest: StatePartIndex(152), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.data", ty: Bool }, + memory: StatePartIndex(7), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x1, + // [0x2]: 0x1, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x1, + // [0x6]: 0x0, + // [0x7]: 0x1, + // [0x8]: 0x1, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x1, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(88), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 72: Branch { + target: 74, + }, + 73: Const { + dest: StatePartIndex(152), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.data", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 74: Copy { + dest: StatePartIndex(31), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[7].data", ty: Bool }, + src: StatePartIndex(152), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.data", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 75: IsNonZeroDestIsSmall { + dest: StatePartIndex(86), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(151), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::r0.clk", ty: Clock }, + }, + 76: AndSmall { + dest: StatePartIndex(85), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(86), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(84), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 77: CastBigToArrayIndex { + dest: StatePartIndex(81), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(142), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.addr", ty: UInt<4> }, + }, + 78: IsNonZeroDestIsSmall { + dest: StatePartIndex(80), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(143), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.en", ty: Bool }, + }, + 79: IsNonZeroDestIsSmall { + dest: StatePartIndex(79), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(144), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.clk", ty: Clock }, + }, + 80: AndSmall { + dest: StatePartIndex(78), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(79), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(77), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 81: CastBigToArrayIndex { + dest: StatePartIndex(76), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(138), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.addr", ty: UInt<4> }, + }, + 82: IsNonZeroDestIsSmall { + dest: StatePartIndex(75), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(139), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.en", ty: Bool }, + }, + 83: BranchIfSmallZero { + target: 86, + value: StatePartIndex(75), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 84: MemoryReadUInt { + dest: StatePartIndex(141), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.data", ty: Bool }, + memory: StatePartIndex(6), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x1, + // [0x6]: 0x1, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x1, + // [0xa]: 0x1, + // [0xb]: 0x1, + // [0xc]: 0x1, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(76), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 85: Branch { + target: 87, + }, + 86: Const { + dest: StatePartIndex(141), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.data", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 87: Copy { + dest: StatePartIndex(27), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[6].data", ty: Bool }, + src: StatePartIndex(141), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.data", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 88: IsNonZeroDestIsSmall { + dest: StatePartIndex(74), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(140), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::r0.clk", ty: Clock }, + }, + 89: AndSmall { + dest: StatePartIndex(73), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(74), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(72), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 90: CastBigToArrayIndex { + dest: StatePartIndex(69), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(131), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.addr", ty: UInt<4> }, + }, + 91: IsNonZeroDestIsSmall { + dest: StatePartIndex(68), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(132), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.en", ty: Bool }, + }, + 92: IsNonZeroDestIsSmall { + dest: StatePartIndex(67), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(133), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.clk", ty: Clock }, + }, + 93: AndSmall { + dest: StatePartIndex(66), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(67), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(65), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 94: CastBigToArrayIndex { + dest: StatePartIndex(64), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(127), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.addr", ty: UInt<4> }, + }, + 95: IsNonZeroDestIsSmall { + dest: StatePartIndex(63), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(128), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.en", ty: Bool }, + }, + 96: BranchIfSmallZero { + target: 99, + value: StatePartIndex(63), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 97: MemoryReadUInt { + dest: StatePartIndex(130), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.data", ty: Bool }, + memory: StatePartIndex(5), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x1, + // [0x3]: 0x0, + // [0x4]: 0x1, + // [0x5]: 0x1, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x1, + // [0xb]: 0x1, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(64), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 98: Branch { + target: 100, + }, + 99: Const { + dest: StatePartIndex(130), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.data", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 100: Copy { + dest: StatePartIndex(23), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[5].data", ty: Bool }, + src: StatePartIndex(130), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.data", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 101: IsNonZeroDestIsSmall { + dest: StatePartIndex(62), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(129), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::r0.clk", ty: Clock }, + }, + 102: AndSmall { + dest: StatePartIndex(61), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(62), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(60), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 103: CastBigToArrayIndex { + dest: StatePartIndex(57), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(120), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.addr", ty: UInt<4> }, + }, + 104: IsNonZeroDestIsSmall { + dest: StatePartIndex(56), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(121), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.en", ty: Bool }, + }, + 105: IsNonZeroDestIsSmall { + dest: StatePartIndex(55), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(122), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.clk", ty: Clock }, + }, + 106: AndSmall { + dest: StatePartIndex(54), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(55), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(53), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 107: CastBigToArrayIndex { + dest: StatePartIndex(52), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(116), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.addr", ty: UInt<4> }, + }, + 108: IsNonZeroDestIsSmall { + dest: StatePartIndex(51), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(117), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.en", ty: Bool }, + }, + 109: BranchIfSmallZero { + target: 112, + value: StatePartIndex(51), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 110: MemoryReadUInt { + dest: StatePartIndex(119), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.data", ty: Bool }, + memory: StatePartIndex(4), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x0, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x1, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(52), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 111: Branch { + target: 113, + }, + 112: Const { + dest: StatePartIndex(119), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.data", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 113: Copy { + dest: StatePartIndex(19), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[4].data", ty: Bool }, + src: StatePartIndex(119), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.data", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 114: IsNonZeroDestIsSmall { + dest: StatePartIndex(50), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(118), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::r0.clk", ty: Clock }, + }, + 115: AndSmall { + dest: StatePartIndex(49), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(50), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(48), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 116: CastBigToArrayIndex { + dest: StatePartIndex(45), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(109), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.addr", ty: UInt<4> }, + }, + 117: IsNonZeroDestIsSmall { + dest: StatePartIndex(44), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(110), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.en", ty: Bool }, + }, + 118: IsNonZeroDestIsSmall { + dest: StatePartIndex(43), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(111), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.clk", ty: Clock }, + }, + 119: AndSmall { + dest: StatePartIndex(42), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(43), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(41), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 120: CastBigToArrayIndex { + dest: StatePartIndex(40), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(105), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.addr", ty: UInt<4> }, + }, + 121: IsNonZeroDestIsSmall { + dest: StatePartIndex(39), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(106), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.en", ty: Bool }, + }, + 122: BranchIfSmallZero { + target: 125, + value: StatePartIndex(39), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 123: MemoryReadUInt { + dest: StatePartIndex(108), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.data", ty: Bool }, + memory: StatePartIndex(3), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x1, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x1, + // [0x5]: 0x1, + // [0x6]: 0x1, + // [0x7]: 0x1, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(40), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 124: Branch { + target: 126, + }, + 125: Const { + dest: StatePartIndex(108), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.data", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 126: Copy { + dest: StatePartIndex(15), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[3].data", ty: Bool }, + src: StatePartIndex(108), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.data", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 127: IsNonZeroDestIsSmall { + dest: StatePartIndex(38), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(107), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::r0.clk", ty: Clock }, + }, + 128: AndSmall { + dest: StatePartIndex(37), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(38), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(36), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 129: CastBigToArrayIndex { + dest: StatePartIndex(33), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(98), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.addr", ty: UInt<4> }, + }, + 130: IsNonZeroDestIsSmall { + dest: StatePartIndex(32), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(99), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.en", ty: Bool }, + }, + 131: IsNonZeroDestIsSmall { + dest: StatePartIndex(31), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(100), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.clk", ty: Clock }, + }, + 132: AndSmall { + dest: StatePartIndex(30), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(31), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(29), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 133: CastBigToArrayIndex { + dest: StatePartIndex(28), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(94), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.addr", ty: UInt<4> }, + }, + 134: IsNonZeroDestIsSmall { + dest: StatePartIndex(27), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(95), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.en", ty: Bool }, + }, + 135: BranchIfSmallZero { + target: 138, + value: StatePartIndex(27), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 136: MemoryReadUInt { + dest: StatePartIndex(97), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.data", ty: Bool }, + memory: StatePartIndex(2), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x1, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(28), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 137: Branch { + target: 139, + }, + 138: Const { + dest: StatePartIndex(97), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.data", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 139: Copy { + dest: StatePartIndex(11), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[2].data", ty: Bool }, + src: StatePartIndex(97), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.data", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 140: IsNonZeroDestIsSmall { + dest: StatePartIndex(26), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(96), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::r0.clk", ty: Clock }, + }, + 141: AndSmall { + dest: StatePartIndex(25), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(26), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(24), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 142: CastBigToArrayIndex { + dest: StatePartIndex(21), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(87), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.addr", ty: UInt<4> }, + }, + 143: IsNonZeroDestIsSmall { + dest: StatePartIndex(20), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(88), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.en", ty: Bool }, + }, + 144: IsNonZeroDestIsSmall { + dest: StatePartIndex(19), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(89), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.clk", ty: Clock }, + }, + 145: AndSmall { + dest: StatePartIndex(18), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(19), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(17), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 146: CastBigToArrayIndex { + dest: StatePartIndex(16), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(83), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.addr", ty: UInt<4> }, + }, + 147: IsNonZeroDestIsSmall { + dest: StatePartIndex(15), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(84), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.en", ty: Bool }, + }, + 148: BranchIfSmallZero { + target: 151, + value: StatePartIndex(15), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 149: MemoryReadUInt { + dest: StatePartIndex(86), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.data", ty: Bool }, + memory: StatePartIndex(1), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x0, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(16), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 150: Branch { + target: 152, + }, + 151: Const { + dest: StatePartIndex(86), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.data", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 152: Copy { + dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[1].data", ty: Bool }, + src: StatePartIndex(86), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.data", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 153: IsNonZeroDestIsSmall { + dest: StatePartIndex(14), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(85), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::r0.clk", ty: Clock }, + }, + 154: AndSmall { + dest: StatePartIndex(13), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(14), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(12), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 155: CastBigToArrayIndex { + dest: StatePartIndex(9), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(76), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.addr", ty: UInt<4> }, + }, + 156: IsNonZeroDestIsSmall { + dest: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(77), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.en", ty: Bool }, + }, + 157: IsNonZeroDestIsSmall { + dest: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(78), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.clk", ty: Clock }, + }, + 158: AndSmall { + dest: StatePartIndex(6), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(5), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 159: CastBigToArrayIndex { + dest: StatePartIndex(4), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(72), // (0xf) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.addr", ty: UInt<4> }, + }, + 160: IsNonZeroDestIsSmall { + dest: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(73), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.en", ty: Bool }, + }, + 161: BranchIfSmallZero { + target: 164, + value: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 162: MemoryReadUInt { + dest: StatePartIndex(75), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.data", ty: Bool }, + memory: StatePartIndex(0), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x0, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(4), // (0xf 15) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 163: Branch { + target: 165, + }, + 164: Const { + dest: StatePartIndex(75), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.data", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 165: Copy { + dest: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::r[0].data", ty: Bool }, + src: StatePartIndex(75), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.data", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 166: IsNonZeroDestIsSmall { + dest: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(74), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::r0.clk", ty: Clock }, + }, + 167: AndSmall { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(0), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 168: BranchIfSmallZero { + target: 169, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 169: BranchIfSmallZero { + target: 177, + value: StatePartIndex(6), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 170: CopySmall { + dest: StatePartIndex(10), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(9), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 171: CopySmall { + dest: StatePartIndex(11), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 172: Copy { + dest: StatePartIndex(81), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(79), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.data", ty: Bool }, + }, + 173: Copy { + dest: StatePartIndex(82), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(80), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_0::w1.mask", ty: Bool }, + }, + 174: BranchIfSmallZero { + target: 177, + value: StatePartIndex(11), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 175: BranchIfZero { + target: 177, + value: StatePartIndex(82), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + 176: MemoryWriteUInt { + value: StatePartIndex(81), // (0x0) SlotDebugData { name: "", ty: Bool }, + memory: StatePartIndex(0), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x0, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(10), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 177: BranchIfSmallZero { + target: 178, + value: StatePartIndex(13), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 178: BranchIfSmallZero { + target: 186, + value: StatePartIndex(18), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 179: CopySmall { + dest: StatePartIndex(22), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(21), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 180: CopySmall { + dest: StatePartIndex(23), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(20), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 181: Copy { + dest: StatePartIndex(92), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(90), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.data", ty: Bool }, + }, + 182: Copy { + dest: StatePartIndex(93), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(91), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_1::w1.mask", ty: Bool }, + }, + 183: BranchIfSmallZero { + target: 186, + value: StatePartIndex(23), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 184: BranchIfZero { + target: 186, + value: StatePartIndex(93), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + 185: MemoryWriteUInt { + value: StatePartIndex(92), // (0x0) SlotDebugData { name: "", ty: Bool }, + memory: StatePartIndex(1), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x0, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(22), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 186: BranchIfSmallZero { + target: 187, + value: StatePartIndex(25), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 187: BranchIfSmallZero { + target: 195, + value: StatePartIndex(30), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 188: CopySmall { + dest: StatePartIndex(34), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(33), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 189: CopySmall { + dest: StatePartIndex(35), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(32), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 190: Copy { + dest: StatePartIndex(103), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(101), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.data", ty: Bool }, + }, + 191: Copy { + dest: StatePartIndex(104), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(102), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_2::w1.mask", ty: Bool }, + }, + 192: BranchIfSmallZero { + target: 195, + value: StatePartIndex(35), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 193: BranchIfZero { + target: 195, + value: StatePartIndex(104), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + 194: MemoryWriteUInt { + value: StatePartIndex(103), // (0x0) SlotDebugData { name: "", ty: Bool }, + memory: StatePartIndex(2), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x1, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(34), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 195: BranchIfSmallZero { + target: 196, + value: StatePartIndex(37), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 196: BranchIfSmallZero { + target: 204, + value: StatePartIndex(42), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 197: CopySmall { + dest: StatePartIndex(46), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(45), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 198: CopySmall { + dest: StatePartIndex(47), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(44), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 199: Copy { + dest: StatePartIndex(114), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(112), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.data", ty: Bool }, + }, + 200: Copy { + dest: StatePartIndex(115), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(113), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_3::w1.mask", ty: Bool }, + }, + 201: BranchIfSmallZero { + target: 204, + value: StatePartIndex(47), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 202: BranchIfZero { + target: 204, + value: StatePartIndex(115), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + 203: MemoryWriteUInt { + value: StatePartIndex(114), // (0x0) SlotDebugData { name: "", ty: Bool }, + memory: StatePartIndex(3), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x1, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x1, + // [0x5]: 0x1, + // [0x6]: 0x1, + // [0x7]: 0x1, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(46), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 204: BranchIfSmallZero { + target: 205, + value: StatePartIndex(49), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 205: BranchIfSmallZero { + target: 213, + value: StatePartIndex(54), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 206: CopySmall { + dest: StatePartIndex(58), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(57), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 207: CopySmall { + dest: StatePartIndex(59), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(56), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 208: Copy { + dest: StatePartIndex(125), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(123), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.data", ty: Bool }, + }, + 209: Copy { + dest: StatePartIndex(126), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(124), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_4::w1.mask", ty: Bool }, + }, + 210: BranchIfSmallZero { + target: 213, + value: StatePartIndex(59), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 211: BranchIfZero { + target: 213, + value: StatePartIndex(126), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + 212: MemoryWriteUInt { + value: StatePartIndex(125), // (0x0) SlotDebugData { name: "", ty: Bool }, + memory: StatePartIndex(4), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x0, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x1, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(58), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 213: BranchIfSmallZero { + target: 214, + value: StatePartIndex(61), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 214: BranchIfSmallZero { + target: 222, + value: StatePartIndex(66), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 215: CopySmall { + dest: StatePartIndex(70), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(69), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 216: CopySmall { + dest: StatePartIndex(71), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(68), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 217: Copy { + dest: StatePartIndex(136), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(134), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.data", ty: Bool }, + }, + 218: Copy { + dest: StatePartIndex(137), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(135), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_5::w1.mask", ty: Bool }, + }, + 219: BranchIfSmallZero { + target: 222, + value: StatePartIndex(71), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 220: BranchIfZero { + target: 222, + value: StatePartIndex(137), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + 221: MemoryWriteUInt { + value: StatePartIndex(136), // (0x0) SlotDebugData { name: "", ty: Bool }, + memory: StatePartIndex(5), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x1, + // [0x3]: 0x0, + // [0x4]: 0x1, + // [0x5]: 0x1, + // [0x6]: 0x0, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x0, + // [0xa]: 0x1, + // [0xb]: 0x1, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(70), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 222: BranchIfSmallZero { + target: 223, + value: StatePartIndex(73), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 223: BranchIfSmallZero { + target: 231, + value: StatePartIndex(78), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 224: CopySmall { + dest: StatePartIndex(82), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(81), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 225: CopySmall { + dest: StatePartIndex(83), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(80), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 226: Copy { + dest: StatePartIndex(147), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(145), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.data", ty: Bool }, + }, + 227: Copy { + dest: StatePartIndex(148), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(146), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_6::w1.mask", ty: Bool }, + }, + 228: BranchIfSmallZero { + target: 231, + value: StatePartIndex(83), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 229: BranchIfZero { + target: 231, + value: StatePartIndex(148), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + 230: MemoryWriteUInt { + value: StatePartIndex(147), // (0x0) SlotDebugData { name: "", ty: Bool }, + memory: StatePartIndex(6), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x0, + // [0x2]: 0x0, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x1, + // [0x6]: 0x1, + // [0x7]: 0x0, + // [0x8]: 0x0, + // [0x9]: 0x1, + // [0xa]: 0x1, + // [0xb]: 0x1, + // [0xc]: 0x1, + // [0xd]: 0x0, + // [0xe]: 0x0, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(82), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 231: BranchIfSmallZero { + target: 232, + value: StatePartIndex(85), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 232: BranchIfSmallZero { + target: 240, + value: StatePartIndex(90), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 233: CopySmall { + dest: StatePartIndex(94), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(93), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 234: CopySmall { + dest: StatePartIndex(95), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(92), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 235: Copy { + dest: StatePartIndex(158), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(156), // (0x0) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.data", ty: Bool }, + }, + 236: Copy { + dest: StatePartIndex(159), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(157), // (0x1) SlotDebugData { name: "InstantiatedModule(many_memories: many_memories).many_memories::mem_7::w1.mask", ty: Bool }, + }, + 237: BranchIfSmallZero { + target: 240, + value: StatePartIndex(95), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 238: BranchIfZero { + target: 240, + value: StatePartIndex(159), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + 239: MemoryWriteUInt { + value: StatePartIndex(158), // (0x0) SlotDebugData { name: "", ty: Bool }, + memory: StatePartIndex(7), // (MemoryData { + // array_type: Array, + // data: [ + // // len = 0x10 + // [0x0]: 0x0, + // [0x1]: 0x1, + // [0x2]: 0x1, + // [0x3]: 0x0, + // [0x4]: 0x0, + // [0x5]: 0x1, + // [0x6]: 0x0, + // [0x7]: 0x1, + // [0x8]: 0x1, + // [0x9]: 0x0, + // [0xa]: 0x0, + // [0xb]: 0x0, + // [0xc]: 0x0, + // [0xd]: 0x0, + // [0xe]: 0x1, + // [0xf]: 0x0, + // ], + // }) (), + addr: StatePartIndex(94), // (0x0 0) SlotDebugData { name: "", ty: UInt<4> }, + stride: 1, + start: 0, + width: 1, + }, + 240: XorSmallImmediate { + dest: StatePartIndex(0), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 241: XorSmallImmediate { + dest: StatePartIndex(5), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 242: XorSmallImmediate { + dest: StatePartIndex(12), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(14), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 243: XorSmallImmediate { + dest: StatePartIndex(17), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(19), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 244: XorSmallImmediate { + dest: StatePartIndex(24), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(26), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 245: XorSmallImmediate { + dest: StatePartIndex(29), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(31), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 246: XorSmallImmediate { + dest: StatePartIndex(36), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(38), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 247: XorSmallImmediate { + dest: StatePartIndex(41), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(43), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 248: XorSmallImmediate { + dest: StatePartIndex(48), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(50), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 249: XorSmallImmediate { + dest: StatePartIndex(53), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(55), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 250: XorSmallImmediate { + dest: StatePartIndex(60), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(62), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 251: XorSmallImmediate { + dest: StatePartIndex(65), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(67), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 252: XorSmallImmediate { + dest: StatePartIndex(72), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(74), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 253: XorSmallImmediate { + dest: StatePartIndex(77), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(79), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 254: XorSmallImmediate { + dest: StatePartIndex(84), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(86), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 255: XorSmallImmediate { + dest: StatePartIndex(89), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(91), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 256: Return, + ], + .. + }, + pc: 256, + memory_write_log: [], + memories: StatePart { + value: [ + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x0, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x0, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x1, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x1, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x1, + [0x5]: 0x1, + [0x6]: 0x1, + [0x7]: 0x1, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x0, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x1, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x1, + [0x3]: 0x0, + [0x4]: 0x1, + [0x5]: 0x1, + [0x6]: 0x0, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x0, + [0xa]: 0x1, + [0xb]: 0x1, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x0, + [0x2]: 0x0, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x1, + [0x6]: 0x1, + [0x7]: 0x0, + [0x8]: 0x0, + [0x9]: 0x1, + [0xa]: 0x1, + [0xb]: 0x1, + [0xc]: 0x1, + [0xd]: 0x0, + [0xe]: 0x0, + [0xf]: 0x0, + ], + }, + MemoryData { + array_type: Array, + data: [ + // len = 0x10 + [0x0]: 0x0, + [0x1]: 0x1, + [0x2]: 0x1, + [0x3]: 0x0, + [0x4]: 0x0, + [0x5]: 0x1, + [0x6]: 0x0, + [0x7]: 0x1, + [0x8]: 0x1, + [0x9]: 0x0, + [0xa]: 0x0, + [0xb]: 0x0, + [0xc]: 0x0, + [0xd]: 0x0, + [0xe]: 0x1, + [0xf]: 0x0, + ], + }, + ], + }, + small_slots: StatePart { + value: [ + 1, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + ], + }, + big_slots: StatePart { + value: [ + 15, + 1, + 0, + 0, + 15, + 1, + 0, + 0, + 15, + 1, + 0, + 0, + 15, + 1, + 0, + 0, + 15, + 1, + 0, + 0, + 15, + 1, + 0, + 0, + 15, + 1, + 0, + 0, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 15, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[0], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[0].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[0].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[0].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[0].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[1], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[1].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[1].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[1].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[1].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[2], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[2].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[2].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[2].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[2].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[3], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[3].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[3].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[3].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[3].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[4], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[4].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[4].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[4].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[4].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[5], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[5].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[5].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[5].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[5].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[6], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[6].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[6].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[6].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[6].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[7], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[7].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[7].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[7].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.r[7].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[0], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[0].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[0].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[0].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[0].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[0].mask, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[1], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[1].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[1].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[1].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[1].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[1].mask, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[2], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[2].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[2].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[2].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[2].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[2].mask, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[3], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[3].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[3].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[3].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[3].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[3].mask, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[4], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[4].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[4].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[4].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[4].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[4].mask, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[5], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[5].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[5].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[5].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[5].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[5].mask, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[6], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[6].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[6].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[6].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[6].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[6].mask, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[7], + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[7].addr, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[7].clk, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[7].data, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[7].en, + Instance { + name: ::many_memories, + instantiated: Module { + name: many_memories, + .. + }, + }.w[7].mask, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [], + trace_decls: TraceModule { + name: "many_memories", + children: [ + TraceModuleIO { + name: "r", + child: TraceArray { + name: "r", + elements: [ + TraceBundle { + name: "[0]", + fields: [ + TraceUInt { + location: TraceScalarId(0), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(1), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(2), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(3), + name: "data", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[1]", + fields: [ + TraceUInt { + location: TraceScalarId(4), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(5), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(6), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(7), + name: "data", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[2]", + fields: [ + TraceUInt { + location: TraceScalarId(8), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(9), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(10), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(11), + name: "data", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[3]", + fields: [ + TraceUInt { + location: TraceScalarId(12), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(13), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(14), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(15), + name: "data", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[4]", + fields: [ + TraceUInt { + location: TraceScalarId(16), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(17), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(18), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(19), + name: "data", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[5]", + fields: [ + TraceUInt { + location: TraceScalarId(20), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(21), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(22), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(23), + name: "data", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[6]", + fields: [ + TraceUInt { + location: TraceScalarId(24), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(25), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(26), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(27), + name: "data", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[7]", + fields: [ + TraceUInt { + location: TraceScalarId(28), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(29), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(30), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(31), + name: "data", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Source, + }, + ], + ty: Array, en: Bool, clk: Clock, #[hdl(flip)] data: Bool}, 8>, + flow: Source, + }, + ty: Array, en: Bool, clk: Clock, #[hdl(flip)] data: Bool}, 8>, + flow: Source, + }, + TraceModuleIO { + name: "w", + child: TraceArray { + name: "w", + elements: [ + TraceBundle { + name: "[0]", + fields: [ + TraceUInt { + location: TraceScalarId(32), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(33), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(34), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(35), + name: "data", + flow: Source, + }, + TraceBool { + location: TraceScalarId(36), + name: "mask", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[1]", + fields: [ + TraceUInt { + location: TraceScalarId(37), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(38), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(39), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(40), + name: "data", + flow: Source, + }, + TraceBool { + location: TraceScalarId(41), + name: "mask", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[2]", + fields: [ + TraceUInt { + location: TraceScalarId(42), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(43), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(44), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(45), + name: "data", + flow: Source, + }, + TraceBool { + location: TraceScalarId(46), + name: "mask", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[3]", + fields: [ + TraceUInt { + location: TraceScalarId(47), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(48), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(49), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(50), + name: "data", + flow: Source, + }, + TraceBool { + location: TraceScalarId(51), + name: "mask", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[4]", + fields: [ + TraceUInt { + location: TraceScalarId(52), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(53), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(54), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(55), + name: "data", + flow: Source, + }, + TraceBool { + location: TraceScalarId(56), + name: "mask", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[5]", + fields: [ + TraceUInt { + location: TraceScalarId(57), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(58), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(59), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(60), + name: "data", + flow: Source, + }, + TraceBool { + location: TraceScalarId(61), + name: "mask", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[6]", + fields: [ + TraceUInt { + location: TraceScalarId(62), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(63), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(64), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(65), + name: "data", + flow: Source, + }, + TraceBool { + location: TraceScalarId(66), + name: "mask", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Source, + }, + TraceBundle { + name: "[7]", + fields: [ + TraceUInt { + location: TraceScalarId(67), + name: "addr", + ty: UInt<4>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(68), + name: "en", + flow: Source, + }, + TraceClock { + location: TraceScalarId(69), + name: "clk", + flow: Source, + }, + TraceBool { + location: TraceScalarId(70), + name: "data", + flow: Source, + }, + TraceBool { + location: TraceScalarId(71), + name: "mask", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Source, + }, + ], + ty: Array, en: Bool, clk: Clock, data: Bool, mask: Bool}, 8>, + flow: Source, + }, + ty: Array, en: Bool, clk: Clock, data: Bool, mask: Bool}, 8>, + flow: Source, + }, + TraceMem { + id: TraceMemoryId(0), + name: "mem_0", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(0), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_0", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(72), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(73), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(74), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(75), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(76), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(77), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(78), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(79), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(80), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + TraceMem { + id: TraceMemoryId(1), + name: "mem_1", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(1), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_1", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(81), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(82), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(83), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(84), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(85), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(86), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(87), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(88), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(89), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + TraceMem { + id: TraceMemoryId(2), + name: "mem_2", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(2), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_2", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(90), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(91), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(92), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(93), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(94), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(95), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(96), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(97), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(98), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + TraceMem { + id: TraceMemoryId(3), + name: "mem_3", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(3), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_3", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(99), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(100), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(101), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(102), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(103), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(104), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(105), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(106), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(107), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + TraceMem { + id: TraceMemoryId(4), + name: "mem_4", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(4), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_4", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(108), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(109), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(110), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(111), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(112), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(113), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(114), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(115), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(116), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + TraceMem { + id: TraceMemoryId(5), + name: "mem_5", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(5), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_5", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(117), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(118), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(119), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(120), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(121), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(122), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(123), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(124), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(125), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + TraceMem { + id: TraceMemoryId(6), + name: "mem_6", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(6), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_6", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(126), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(127), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(128), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(129), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(130), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(131), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(132), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(133), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(134), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + TraceMem { + id: TraceMemoryId(7), + name: "mem_7", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(7), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_7", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(135), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(136), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(137), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(138), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(139), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(140), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(141), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(142), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(143), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigUInt { + index: StatePartIndex(0), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigBool { + index: StatePartIndex(1), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigClock { + index: StatePartIndex(2), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigBool { + index: StatePartIndex(3), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigUInt { + index: StatePartIndex(4), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigBool { + index: StatePartIndex(5), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(6), + kind: BigClock { + index: StatePartIndex(6), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(7), + kind: BigBool { + index: StatePartIndex(7), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(8), + kind: BigUInt { + index: StatePartIndex(8), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(9), + kind: BigBool { + index: StatePartIndex(9), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(10), + kind: BigClock { + index: StatePartIndex(10), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigBool { + index: StatePartIndex(11), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(12), + kind: BigUInt { + index: StatePartIndex(12), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(13), + kind: BigBool { + index: StatePartIndex(13), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(14), + kind: BigClock { + index: StatePartIndex(14), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(15), + kind: BigBool { + index: StatePartIndex(15), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(16), + kind: BigUInt { + index: StatePartIndex(16), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(17), + kind: BigBool { + index: StatePartIndex(17), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(18), + kind: BigClock { + index: StatePartIndex(18), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(19), + kind: BigBool { + index: StatePartIndex(19), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(20), + kind: BigUInt { + index: StatePartIndex(20), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(21), + kind: BigBool { + index: StatePartIndex(21), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(22), + kind: BigClock { + index: StatePartIndex(22), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(23), + kind: BigBool { + index: StatePartIndex(23), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(24), + kind: BigUInt { + index: StatePartIndex(24), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(25), + kind: BigBool { + index: StatePartIndex(25), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(26), + kind: BigClock { + index: StatePartIndex(26), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(27), + kind: BigBool { + index: StatePartIndex(27), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(28), + kind: BigUInt { + index: StatePartIndex(28), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(29), + kind: BigBool { + index: StatePartIndex(29), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(30), + kind: BigClock { + index: StatePartIndex(30), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(31), + kind: BigBool { + index: StatePartIndex(31), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(32), + kind: BigUInt { + index: StatePartIndex(32), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(33), + kind: BigBool { + index: StatePartIndex(33), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(34), + kind: BigClock { + index: StatePartIndex(34), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(35), + kind: BigBool { + index: StatePartIndex(35), + }, + state: 0x0, + last_state: 0x0, + }, + 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<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(38), + kind: BigBool { + index: StatePartIndex(38), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(39), + kind: BigClock { + index: StatePartIndex(39), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(40), + kind: BigBool { + index: StatePartIndex(40), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(41), + kind: BigBool { + index: StatePartIndex(41), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(42), + kind: BigUInt { + index: StatePartIndex(42), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(43), + kind: BigBool { + index: StatePartIndex(43), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(44), + kind: BigClock { + index: StatePartIndex(44), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(45), + kind: BigBool { + index: StatePartIndex(45), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(46), + kind: BigBool { + index: StatePartIndex(46), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(47), + kind: BigUInt { + index: StatePartIndex(47), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(48), + kind: BigBool { + index: StatePartIndex(48), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(49), + kind: BigClock { + index: StatePartIndex(49), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(50), + kind: BigBool { + index: StatePartIndex(50), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(51), + kind: BigBool { + index: StatePartIndex(51), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(52), + kind: BigUInt { + index: StatePartIndex(52), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(53), + kind: BigBool { + index: StatePartIndex(53), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(54), + kind: BigClock { + index: StatePartIndex(54), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(55), + kind: BigBool { + index: StatePartIndex(55), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(56), + kind: BigBool { + index: StatePartIndex(56), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(57), + kind: BigUInt { + index: StatePartIndex(57), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(58), + kind: BigBool { + index: StatePartIndex(58), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(59), + kind: BigClock { + index: StatePartIndex(59), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(60), + kind: BigBool { + index: StatePartIndex(60), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(61), + kind: BigBool { + index: StatePartIndex(61), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(62), + kind: BigUInt { + index: StatePartIndex(62), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(63), + kind: BigBool { + index: StatePartIndex(63), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(64), + kind: BigClock { + index: StatePartIndex(64), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(65), + kind: BigBool { + index: StatePartIndex(65), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(66), + kind: BigBool { + index: StatePartIndex(66), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(67), + kind: BigUInt { + index: StatePartIndex(67), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(68), + kind: BigBool { + index: StatePartIndex(68), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(69), + kind: BigClock { + index: StatePartIndex(69), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(70), + kind: BigBool { + index: StatePartIndex(70), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(71), + kind: BigBool { + index: StatePartIndex(71), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(72), + kind: BigUInt { + index: StatePartIndex(72), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(73), + kind: BigBool { + index: StatePartIndex(73), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(74), + kind: BigClock { + index: StatePartIndex(74), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(75), + kind: BigBool { + index: StatePartIndex(75), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(76), + kind: BigUInt { + index: StatePartIndex(76), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(77), + kind: BigBool { + index: StatePartIndex(77), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(78), + kind: BigClock { + index: StatePartIndex(78), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(79), + kind: BigBool { + index: StatePartIndex(79), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(80), + kind: BigBool { + index: StatePartIndex(80), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(81), + kind: BigUInt { + index: StatePartIndex(83), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(82), + kind: BigBool { + index: StatePartIndex(84), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(83), + kind: BigClock { + index: StatePartIndex(85), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(84), + kind: BigBool { + index: StatePartIndex(86), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(85), + kind: BigUInt { + index: StatePartIndex(87), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(86), + kind: BigBool { + index: StatePartIndex(88), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(87), + kind: BigClock { + index: StatePartIndex(89), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(88), + kind: BigBool { + index: StatePartIndex(90), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(89), + kind: BigBool { + index: StatePartIndex(91), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(90), + kind: BigUInt { + index: StatePartIndex(94), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(91), + kind: BigBool { + index: StatePartIndex(95), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(92), + kind: BigClock { + index: StatePartIndex(96), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(93), + kind: BigBool { + index: StatePartIndex(97), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(94), + kind: BigUInt { + index: StatePartIndex(98), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(95), + kind: BigBool { + index: StatePartIndex(99), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(96), + kind: BigClock { + index: StatePartIndex(100), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(97), + kind: BigBool { + index: StatePartIndex(101), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(98), + kind: BigBool { + index: StatePartIndex(102), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(99), + kind: BigUInt { + index: StatePartIndex(105), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(100), + kind: BigBool { + index: StatePartIndex(106), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(101), + kind: BigClock { + index: StatePartIndex(107), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(102), + kind: BigBool { + index: StatePartIndex(108), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(103), + kind: BigUInt { + index: StatePartIndex(109), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(104), + kind: BigBool { + index: StatePartIndex(110), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(105), + kind: BigClock { + index: StatePartIndex(111), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(106), + kind: BigBool { + index: StatePartIndex(112), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(107), + kind: BigBool { + index: StatePartIndex(113), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(108), + kind: BigUInt { + index: StatePartIndex(116), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(109), + kind: BigBool { + index: StatePartIndex(117), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(110), + kind: BigClock { + index: StatePartIndex(118), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(111), + kind: BigBool { + index: StatePartIndex(119), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(112), + kind: BigUInt { + index: StatePartIndex(120), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(113), + kind: BigBool { + index: StatePartIndex(121), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(114), + kind: BigClock { + index: StatePartIndex(122), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(115), + kind: BigBool { + index: StatePartIndex(123), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(116), + kind: BigBool { + index: StatePartIndex(124), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(117), + kind: BigUInt { + index: StatePartIndex(127), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(118), + kind: BigBool { + index: StatePartIndex(128), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(119), + kind: BigClock { + index: StatePartIndex(129), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(120), + kind: BigBool { + index: StatePartIndex(130), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(121), + kind: BigUInt { + index: StatePartIndex(131), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(122), + kind: BigBool { + index: StatePartIndex(132), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(123), + kind: BigClock { + index: StatePartIndex(133), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(124), + kind: BigBool { + index: StatePartIndex(134), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(125), + kind: BigBool { + index: StatePartIndex(135), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(126), + kind: BigUInt { + index: StatePartIndex(138), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(127), + kind: BigBool { + index: StatePartIndex(139), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(128), + kind: BigClock { + index: StatePartIndex(140), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(129), + kind: BigBool { + index: StatePartIndex(141), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(130), + kind: BigUInt { + index: StatePartIndex(142), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(131), + kind: BigBool { + index: StatePartIndex(143), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(132), + kind: BigClock { + index: StatePartIndex(144), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(133), + kind: BigBool { + index: StatePartIndex(145), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(134), + kind: BigBool { + index: StatePartIndex(146), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(135), + kind: BigUInt { + index: StatePartIndex(149), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(136), + kind: BigBool { + index: StatePartIndex(150), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(137), + kind: BigClock { + index: StatePartIndex(151), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(138), + kind: BigBool { + index: StatePartIndex(152), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(139), + kind: BigUInt { + index: StatePartIndex(153), + ty: UInt<4>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(140), + kind: BigBool { + index: StatePartIndex(154), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(141), + kind: BigClock { + index: StatePartIndex(155), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(142), + kind: BigBool { + index: StatePartIndex(156), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(143), + kind: BigBool { + index: StatePartIndex(157), + }, + state: 0x1, + last_state: 0x1, + }, + ], + trace_memories: { + StatePartIndex(0): TraceMem { + id: TraceMemoryId(0), + name: "mem_0", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(0), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_0", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(72), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(73), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(74), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(75), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(76), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(77), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(78), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(79), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(80), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + StatePartIndex(1): TraceMem { + id: TraceMemoryId(1), + name: "mem_1", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(1), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_1", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(81), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(82), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(83), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(84), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(85), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(86), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(87), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(88), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(89), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + StatePartIndex(2): TraceMem { + id: TraceMemoryId(2), + name: "mem_2", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(2), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_2", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(90), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(91), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(92), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(93), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(94), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(95), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(96), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(97), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(98), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + StatePartIndex(3): TraceMem { + id: TraceMemoryId(3), + name: "mem_3", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(3), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_3", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(99), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(100), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(101), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(102), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(103), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(104), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(105), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(106), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(107), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + StatePartIndex(4): TraceMem { + id: TraceMemoryId(4), + name: "mem_4", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(4), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_4", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(108), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(109), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(110), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(111), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(112), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(113), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(114), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(115), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(116), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + StatePartIndex(5): TraceMem { + id: TraceMemoryId(5), + name: "mem_5", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(5), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_5", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(117), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(118), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(119), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(120), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(121), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(122), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(123), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(124), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(125), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + StatePartIndex(6): TraceMem { + id: TraceMemoryId(6), + name: "mem_6", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(6), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_6", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(126), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(127), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(128), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(129), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(130), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(131), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(132), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(133), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(134), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + StatePartIndex(7): TraceMem { + id: TraceMemoryId(7), + name: "mem_7", + stride: 1, + element_type: TraceBool { + location: TraceMemoryLocation { + id: TraceMemoryId(7), + depth: 16, + stride: 1, + start: 0, + len: 1, + }, + name: "mem_7", + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(135), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(136), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(137), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(138), + name: "data", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + #[hdl(flip)] /* offset = 6 */ + data: Bool, + }, + }, + TraceMemPort { + name: "w1", + bundle: TraceBundle { + name: "w1", + fields: [ + TraceUInt { + location: TraceScalarId(139), + name: "addr", + ty: UInt<4>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(140), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(141), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(142), + name: "data", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(143), + name: "mask", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<4>, + /* offset = 4 */ + en: Bool, + /* offset = 5 */ + clk: Clock, + /* offset = 6 */ + data: Bool, + /* offset = 7 */ + mask: Bool, + }, + }, + ], + array_type: Array, + }, + }, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [ + StatePartIndex(1), + StatePartIndex(6), + StatePartIndex(13), + StatePartIndex(18), + StatePartIndex(25), + StatePartIndex(30), + StatePartIndex(37), + StatePartIndex(42), + StatePartIndex(49), + StatePartIndex(54), + StatePartIndex(61), + StatePartIndex(66), + StatePartIndex(73), + StatePartIndex(78), + StatePartIndex(85), + StatePartIndex(90), + ], + event_queue: EventQueue(EventQueueData { + instant: 38 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/many_memories.vcd b/crates/fayalite/tests/sim/expected/many_memories.vcd new file mode 100644 index 00000000..77d1447a --- /dev/null +++ b/crates/fayalite/tests/sim/expected/many_memories.vcd @@ -0,0 +1,2596 @@ +$timescale 1 ps $end +$scope module many_memories $end +$scope struct r $end +$scope struct \[0] $end +$var wire 4 ! addr $end +$var wire 1 " en $end +$var wire 1 # clk $end +$var wire 1 $ data $end +$upscope $end +$scope struct \[1] $end +$var wire 4 % addr $end +$var wire 1 & en $end +$var wire 1 ' clk $end +$var wire 1 ( data $end +$upscope $end +$scope struct \[2] $end +$var wire 4 ) addr $end +$var wire 1 * en $end +$var wire 1 + clk $end +$var wire 1 , data $end +$upscope $end +$scope struct \[3] $end +$var wire 4 - addr $end +$var wire 1 . en $end +$var wire 1 / clk $end +$var wire 1 0 data $end +$upscope $end +$scope struct \[4] $end +$var wire 4 1 addr $end +$var wire 1 2 en $end +$var wire 1 3 clk $end +$var wire 1 4 data $end +$upscope $end +$scope struct \[5] $end +$var wire 4 5 addr $end +$var wire 1 6 en $end +$var wire 1 7 clk $end +$var wire 1 8 data $end +$upscope $end +$scope struct \[6] $end +$var wire 4 9 addr $end +$var wire 1 : en $end +$var wire 1 ; clk $end +$var wire 1 < data $end +$upscope $end +$scope struct \[7] $end +$var wire 4 = addr $end +$var wire 1 > en $end +$var wire 1 ? clk $end +$var wire 1 @ data $end +$upscope $end +$upscope $end +$scope struct w $end +$scope struct \[0] $end +$var wire 4 A addr $end +$var wire 1 B en $end +$var wire 1 C clk $end +$var wire 1 D data $end +$var wire 1 E mask $end +$upscope $end +$scope struct \[1] $end +$var wire 4 F addr $end +$var wire 1 G en $end +$var wire 1 H clk $end +$var wire 1 I data $end +$var wire 1 J mask $end +$upscope $end +$scope struct \[2] $end +$var wire 4 K addr $end +$var wire 1 L en $end +$var wire 1 M clk $end +$var wire 1 N data $end +$var wire 1 O mask $end +$upscope $end +$scope struct \[3] $end +$var wire 4 P addr $end +$var wire 1 Q en $end +$var wire 1 R clk $end +$var wire 1 S data $end +$var wire 1 T mask $end +$upscope $end +$scope struct \[4] $end +$var wire 4 U addr $end +$var wire 1 V en $end +$var wire 1 W clk $end +$var wire 1 X data $end +$var wire 1 Y mask $end +$upscope $end +$scope struct \[5] $end +$var wire 4 Z addr $end +$var wire 1 [ en $end +$var wire 1 \ clk $end +$var wire 1 ] data $end +$var wire 1 ^ mask $end +$upscope $end +$scope struct \[6] $end +$var wire 4 _ addr $end +$var wire 1 ` en $end +$var wire 1 a clk $end +$var wire 1 b data $end +$var wire 1 c mask $end +$upscope $end +$scope struct \[7] $end +$var wire 4 d addr $end +$var wire 1 e en $end +$var wire 1 f clk $end +$var wire 1 g data $end +$var wire 1 h mask $end +$upscope $end +$upscope $end +$scope struct mem_0 $end +$scope struct contents $end +$scope struct \[0] $end +$var reg 1 S" mem_0 $end +$upscope $end +$scope struct \[1] $end +$var reg 1 T" mem_0 $end +$upscope $end +$scope struct \[2] $end +$var reg 1 U" mem_0 $end +$upscope $end +$scope struct \[3] $end +$var reg 1 V" mem_0 $end +$upscope $end +$scope struct \[4] $end +$var reg 1 W" mem_0 $end +$upscope $end +$scope struct \[5] $end +$var reg 1 X" mem_0 $end +$upscope $end +$scope struct \[6] $end +$var reg 1 Y" mem_0 $end +$upscope $end +$scope struct \[7] $end +$var reg 1 Z" mem_0 $end +$upscope $end +$scope struct \[8] $end +$var reg 1 [" mem_0 $end +$upscope $end +$scope struct \[9] $end +$var reg 1 \" mem_0 $end +$upscope $end +$scope struct \[10] $end +$var reg 1 ]" mem_0 $end +$upscope $end +$scope struct \[11] $end +$var reg 1 ^" mem_0 $end +$upscope $end +$scope struct \[12] $end +$var reg 1 _" mem_0 $end +$upscope $end +$scope struct \[13] $end +$var reg 1 `" mem_0 $end +$upscope $end +$scope struct \[14] $end +$var reg 1 a" mem_0 $end +$upscope $end +$scope struct \[15] $end +$var reg 1 b" mem_0 $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var wire 4 i addr $end +$var wire 1 j en $end +$var wire 1 k clk $end +$var wire 1 l data $end +$upscope $end +$scope struct w1 $end +$var wire 4 m addr $end +$var wire 1 n en $end +$var wire 1 o clk $end +$var wire 1 p data $end +$var wire 1 q mask $end +$upscope $end +$upscope $end +$scope struct mem_1 $end +$scope struct contents $end +$scope struct \[0] $end +$var reg 1 c" mem_1 $end +$upscope $end +$scope struct \[1] $end +$var reg 1 d" mem_1 $end +$upscope $end +$scope struct \[2] $end +$var reg 1 e" mem_1 $end +$upscope $end +$scope struct \[3] $end +$var reg 1 f" mem_1 $end +$upscope $end +$scope struct \[4] $end +$var reg 1 g" mem_1 $end +$upscope $end +$scope struct \[5] $end +$var reg 1 h" mem_1 $end +$upscope $end +$scope struct \[6] $end +$var reg 1 i" mem_1 $end +$upscope $end +$scope struct \[7] $end +$var reg 1 j" mem_1 $end +$upscope $end +$scope struct \[8] $end +$var reg 1 k" mem_1 $end +$upscope $end +$scope struct \[9] $end +$var reg 1 l" mem_1 $end +$upscope $end +$scope struct \[10] $end +$var reg 1 m" mem_1 $end +$upscope $end +$scope struct \[11] $end +$var reg 1 n" mem_1 $end +$upscope $end +$scope struct \[12] $end +$var reg 1 o" mem_1 $end +$upscope $end +$scope struct \[13] $end +$var reg 1 p" mem_1 $end +$upscope $end +$scope struct \[14] $end +$var reg 1 q" mem_1 $end +$upscope $end +$scope struct \[15] $end +$var reg 1 r" mem_1 $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var wire 4 r addr $end +$var wire 1 s en $end +$var wire 1 t clk $end +$var wire 1 u data $end +$upscope $end +$scope struct w1 $end +$var wire 4 v addr $end +$var wire 1 w en $end +$var wire 1 x clk $end +$var wire 1 y data $end +$var wire 1 z mask $end +$upscope $end +$upscope $end +$scope struct mem_2 $end +$scope struct contents $end +$scope struct \[0] $end +$var reg 1 s" mem_2 $end +$upscope $end +$scope struct \[1] $end +$var reg 1 t" mem_2 $end +$upscope $end +$scope struct \[2] $end +$var reg 1 u" mem_2 $end +$upscope $end +$scope struct \[3] $end +$var reg 1 v" mem_2 $end +$upscope $end +$scope struct \[4] $end +$var reg 1 w" mem_2 $end +$upscope $end +$scope struct \[5] $end +$var reg 1 x" mem_2 $end +$upscope $end +$scope struct \[6] $end +$var reg 1 y" mem_2 $end +$upscope $end +$scope struct \[7] $end +$var reg 1 z" mem_2 $end +$upscope $end +$scope struct \[8] $end +$var reg 1 {" mem_2 $end +$upscope $end +$scope struct \[9] $end +$var reg 1 |" mem_2 $end +$upscope $end +$scope struct \[10] $end +$var reg 1 }" mem_2 $end +$upscope $end +$scope struct \[11] $end +$var reg 1 ~" mem_2 $end +$upscope $end +$scope struct \[12] $end +$var reg 1 !# mem_2 $end +$upscope $end +$scope struct \[13] $end +$var reg 1 "# mem_2 $end +$upscope $end +$scope struct \[14] $end +$var reg 1 ## mem_2 $end +$upscope $end +$scope struct \[15] $end +$var reg 1 $# mem_2 $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var wire 4 { addr $end +$var wire 1 | en $end +$var wire 1 } clk $end +$var wire 1 ~ data $end +$upscope $end +$scope struct w1 $end +$var wire 4 !" addr $end +$var wire 1 "" en $end +$var wire 1 #" clk $end +$var wire 1 $" data $end +$var wire 1 %" mask $end +$upscope $end +$upscope $end +$scope struct mem_3 $end +$scope struct contents $end +$scope struct \[0] $end +$var reg 1 %# mem_3 $end +$upscope $end +$scope struct \[1] $end +$var reg 1 &# mem_3 $end +$upscope $end +$scope struct \[2] $end +$var reg 1 '# mem_3 $end +$upscope $end +$scope struct \[3] $end +$var reg 1 (# mem_3 $end +$upscope $end +$scope struct \[4] $end +$var reg 1 )# mem_3 $end +$upscope $end +$scope struct \[5] $end +$var reg 1 *# mem_3 $end +$upscope $end +$scope struct \[6] $end +$var reg 1 +# mem_3 $end +$upscope $end +$scope struct \[7] $end +$var reg 1 ,# mem_3 $end +$upscope $end +$scope struct \[8] $end +$var reg 1 -# mem_3 $end +$upscope $end +$scope struct \[9] $end +$var reg 1 .# mem_3 $end +$upscope $end +$scope struct \[10] $end +$var reg 1 /# mem_3 $end +$upscope $end +$scope struct \[11] $end +$var reg 1 0# mem_3 $end +$upscope $end +$scope struct \[12] $end +$var reg 1 1# mem_3 $end +$upscope $end +$scope struct \[13] $end +$var reg 1 2# mem_3 $end +$upscope $end +$scope struct \[14] $end +$var reg 1 3# mem_3 $end +$upscope $end +$scope struct \[15] $end +$var reg 1 4# mem_3 $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var wire 4 &" addr $end +$var wire 1 '" en $end +$var wire 1 (" clk $end +$var wire 1 )" data $end +$upscope $end +$scope struct w1 $end +$var wire 4 *" addr $end +$var wire 1 +" en $end +$var wire 1 ," clk $end +$var wire 1 -" data $end +$var wire 1 ." mask $end +$upscope $end +$upscope $end +$scope struct mem_4 $end +$scope struct contents $end +$scope struct \[0] $end +$var reg 1 5# mem_4 $end +$upscope $end +$scope struct \[1] $end +$var reg 1 6# mem_4 $end +$upscope $end +$scope struct \[2] $end +$var reg 1 7# mem_4 $end +$upscope $end +$scope struct \[3] $end +$var reg 1 8# mem_4 $end +$upscope $end +$scope struct \[4] $end +$var reg 1 9# mem_4 $end +$upscope $end +$scope struct \[5] $end +$var reg 1 :# mem_4 $end +$upscope $end +$scope struct \[6] $end +$var reg 1 ;# mem_4 $end +$upscope $end +$scope struct \[7] $end +$var reg 1 <# mem_4 $end +$upscope $end +$scope struct \[8] $end +$var reg 1 =# mem_4 $end +$upscope $end +$scope struct \[9] $end +$var reg 1 ># mem_4 $end +$upscope $end +$scope struct \[10] $end +$var reg 1 ?# mem_4 $end +$upscope $end +$scope struct \[11] $end +$var reg 1 @# mem_4 $end +$upscope $end +$scope struct \[12] $end +$var reg 1 A# mem_4 $end +$upscope $end +$scope struct \[13] $end +$var reg 1 B# mem_4 $end +$upscope $end +$scope struct \[14] $end +$var reg 1 C# mem_4 $end +$upscope $end +$scope struct \[15] $end +$var reg 1 D# mem_4 $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var wire 4 /" addr $end +$var wire 1 0" en $end +$var wire 1 1" clk $end +$var wire 1 2" data $end +$upscope $end +$scope struct w1 $end +$var wire 4 3" addr $end +$var wire 1 4" en $end +$var wire 1 5" clk $end +$var wire 1 6" data $end +$var wire 1 7" mask $end +$upscope $end +$upscope $end +$scope struct mem_5 $end +$scope struct contents $end +$scope struct \[0] $end +$var reg 1 E# mem_5 $end +$upscope $end +$scope struct \[1] $end +$var reg 1 F# mem_5 $end +$upscope $end +$scope struct \[2] $end +$var reg 1 G# mem_5 $end +$upscope $end +$scope struct \[3] $end +$var reg 1 H# mem_5 $end +$upscope $end +$scope struct \[4] $end +$var reg 1 I# mem_5 $end +$upscope $end +$scope struct \[5] $end +$var reg 1 J# mem_5 $end +$upscope $end +$scope struct \[6] $end +$var reg 1 K# mem_5 $end +$upscope $end +$scope struct \[7] $end +$var reg 1 L# mem_5 $end +$upscope $end +$scope struct \[8] $end +$var reg 1 M# mem_5 $end +$upscope $end +$scope struct \[9] $end +$var reg 1 N# mem_5 $end +$upscope $end +$scope struct \[10] $end +$var reg 1 O# mem_5 $end +$upscope $end +$scope struct \[11] $end +$var reg 1 P# mem_5 $end +$upscope $end +$scope struct \[12] $end +$var reg 1 Q# mem_5 $end +$upscope $end +$scope struct \[13] $end +$var reg 1 R# mem_5 $end +$upscope $end +$scope struct \[14] $end +$var reg 1 S# mem_5 $end +$upscope $end +$scope struct \[15] $end +$var reg 1 T# mem_5 $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var wire 4 8" addr $end +$var wire 1 9" en $end +$var wire 1 :" clk $end +$var wire 1 ;" data $end +$upscope $end +$scope struct w1 $end +$var wire 4 <" addr $end +$var wire 1 =" en $end +$var wire 1 >" clk $end +$var wire 1 ?" data $end +$var wire 1 @" mask $end +$upscope $end +$upscope $end +$scope struct mem_6 $end +$scope struct contents $end +$scope struct \[0] $end +$var reg 1 U# mem_6 $end +$upscope $end +$scope struct \[1] $end +$var reg 1 V# mem_6 $end +$upscope $end +$scope struct \[2] $end +$var reg 1 W# mem_6 $end +$upscope $end +$scope struct \[3] $end +$var reg 1 X# mem_6 $end +$upscope $end +$scope struct \[4] $end +$var reg 1 Y# mem_6 $end +$upscope $end +$scope struct \[5] $end +$var reg 1 Z# mem_6 $end +$upscope $end +$scope struct \[6] $end +$var reg 1 [# mem_6 $end +$upscope $end +$scope struct \[7] $end +$var reg 1 \# mem_6 $end +$upscope $end +$scope struct \[8] $end +$var reg 1 ]# mem_6 $end +$upscope $end +$scope struct \[9] $end +$var reg 1 ^# mem_6 $end +$upscope $end +$scope struct \[10] $end +$var reg 1 _# mem_6 $end +$upscope $end +$scope struct \[11] $end +$var reg 1 `# mem_6 $end +$upscope $end +$scope struct \[12] $end +$var reg 1 a# mem_6 $end +$upscope $end +$scope struct \[13] $end +$var reg 1 b# mem_6 $end +$upscope $end +$scope struct \[14] $end +$var reg 1 c# mem_6 $end +$upscope $end +$scope struct \[15] $end +$var reg 1 d# mem_6 $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var wire 4 A" addr $end +$var wire 1 B" en $end +$var wire 1 C" clk $end +$var wire 1 D" data $end +$upscope $end +$scope struct w1 $end +$var wire 4 E" addr $end +$var wire 1 F" en $end +$var wire 1 G" clk $end +$var wire 1 H" data $end +$var wire 1 I" mask $end +$upscope $end +$upscope $end +$scope struct mem_7 $end +$scope struct contents $end +$scope struct \[0] $end +$var reg 1 e# mem_7 $end +$upscope $end +$scope struct \[1] $end +$var reg 1 f# mem_7 $end +$upscope $end +$scope struct \[2] $end +$var reg 1 g# mem_7 $end +$upscope $end +$scope struct \[3] $end +$var reg 1 h# mem_7 $end +$upscope $end +$scope struct \[4] $end +$var reg 1 i# mem_7 $end +$upscope $end +$scope struct \[5] $end +$var reg 1 j# mem_7 $end +$upscope $end +$scope struct \[6] $end +$var reg 1 k# mem_7 $end +$upscope $end +$scope struct \[7] $end +$var reg 1 l# mem_7 $end +$upscope $end +$scope struct \[8] $end +$var reg 1 m# mem_7 $end +$upscope $end +$scope struct \[9] $end +$var reg 1 n# mem_7 $end +$upscope $end +$scope struct \[10] $end +$var reg 1 o# mem_7 $end +$upscope $end +$scope struct \[11] $end +$var reg 1 p# mem_7 $end +$upscope $end +$scope struct \[12] $end +$var reg 1 q# mem_7 $end +$upscope $end +$scope struct \[13] $end +$var reg 1 r# mem_7 $end +$upscope $end +$scope struct \[14] $end +$var reg 1 s# mem_7 $end +$upscope $end +$scope struct \[15] $end +$var reg 1 t# mem_7 $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var wire 4 J" addr $end +$var wire 1 K" en $end +$var wire 1 L" clk $end +$var wire 1 M" data $end +$upscope $end +$scope struct w1 $end +$var wire 4 N" addr $end +$var wire 1 O" en $end +$var wire 1 P" clk $end +$var wire 1 Q" data $end +$var wire 1 R" mask $end +$upscope $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +0S" +0T" +0U" +0V" +0W" +0X" +0Y" +0Z" +0[" +0\" +0]" +0^" +0_" +0`" +0a" +0b" +1c" +0d" +0e" +0f" +0g" +0h" +0i" +0j" +0k" +0l" +0m" +0n" +0o" +0p" +0q" +0r" +0s" +0t" +0u" +0v" +0w" +1x" +0y" +0z" +0{" +0|" +0}" +0~" +0!# +0"# +0## +0$# +1%# +1&# +0'# +0(# +1)# +1*# +1+# +1,# +0-# +0.# +0/# +00# +01# +02# +03# +04# +05# +06# +07# +08# +09# +0:# +0;# +0<# +0=# +0># +1?# +0@# +0A# +0B# +0C# +0D# +1E# +0F# +1G# +0H# +1I# +1J# +0K# +0L# +0M# +0N# +1O# +1P# +0Q# +0R# +0S# +0T# +0U# +0V# +0W# +0X# +0Y# +1Z# +1[# +0\# +0]# +1^# +1_# +1`# +1a# +0b# +0c# +0d# +1e# +1f# +1g# +0h# +0i# +1j# +0k# +1l# +1m# +0n# +0o# +0p# +0q# +0r# +1s# +0t# +b0 ! +0" +0# +0$ +b0 % +0& +0' +0( +b0 ) +0* +0+ +0, +b0 - +0. +0/ +00 +b0 1 +02 +03 +04 +b0 5 +06 +07 +08 +b0 9 +0: +0; +0< +b0 = +0> +0? +0@ +b0 A +0B +0C +0D +0E +b0 F +0G +0H +0I +0J +b0 K +0L +0M +0N +0O +b0 P +0Q +0R +0S +0T +b0 U +0V +0W +0X +0Y +b0 Z +0[ +0\ +0] +0^ +b0 _ +0` +0a +0b +0c +b0 d +0e +0f +0g +0h +b0 i +0j +0k +0l +b0 m +0n +0o +0p +0q +b0 r +0s +0t +0u +b0 v +0w +0x +0y +0z +b0 { +0| +0} +0~ +b0 !" +0"" +0#" +0$" +0%" +b0 &" +0'" +0(" +0)" +b0 *" +0+" +0," +0-" +0." +b0 /" +00" +01" +02" +b0 3" +04" +05" +06" +07" +b0 8" +09" +0:" +0;" +b0 <" +0=" +0>" +0?" +0@" +b0 A" +0B" +0C" +0D" +b0 E" +0F" +0G" +0H" +0I" +b0 J" +0K" +0L" +0M" +b0 N" +0O" +0P" +0Q" +0R" +$end +#1000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#2000000 +1" +0# +1& +0' +1( +1* +0+ +1. +0/ +10 +12 +03 +16 +07 +18 +1: +0; +1> +0? +1@ +1B +0C +1D +1E +1G +0H +1I +1J +1L +0M +1N +1O +1Q +0R +1S +1T +1V +0W +1X +1Y +1[ +0\ +1] +1^ +1` +0a +1b +1c +1e +0f +1g +1h +1j +0k +1n +0o +1p +1q +1s +0t +1u +1w +0x +1y +1z +1| +0} +1"" +0#" +1$" +1%" +1'" +0(" +1)" +1+" +0," +1-" +1." +10" +01" +14" +05" +16" +17" +19" +0:" +1;" +1=" +0>" +1?" +1@" +1B" +0C" +1F" +0G" +1H" +1I" +1K" +0L" +1M" +1O" +0P" +1Q" +1R" +#3000000 +1S" +1c" +1s" +1%# +15# +1E# +1U# +1e# +1# +1$ +1' +1+ +1, +1/ +13 +14 +17 +1; +1< +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1l +1o +1t +1x +1} +1~ +1#" +1(" +1," +11" +12" +15" +1:" +1>" +1C" +1D" +1G" +1L" +1P" +#4000000 +0# +0' +0+ +0/ +03 +07 +0; +0? +0C +0D +0H +0I +0M +0N +0R +0S +0W +0X +0\ +0] +0a +0b +0f +0g +0k +0o +0p +0t +0x +0y +0} +0#" +0$" +0(" +0," +0-" +01" +05" +06" +0:" +0>" +0?" +0C" +0G" +0H" +0L" +0P" +0Q" +#5000000 +0S" +0c" +0s" +0%# +05# +0E# +0U# +0e# +1# +0$ +1' +0( +1+ +0, +1/ +00 +13 +04 +17 +08 +1; +0< +1? +0@ +1C +1H +1M +1R +1W +1\ +1a +1f +1k +0l +1o +1t +0u +1x +1} +0~ +1#" +1(" +0)" +1," +11" +02" +15" +1:" +0;" +1>" +1C" +0D" +1G" +1L" +0M" +1P" +#6000000 +0# +0' +0+ +0/ +03 +07 +0; +0? +0B +0C +0G +0H +0L +0M +0Q +0R +0V +0W +0[ +0\ +0` +0a +0e +0f +0k +0n +0o +0t +0w +0x +0} +0"" +0#" +0(" +0+" +0," +01" +04" +05" +0:" +0=" +0>" +0C" +0F" +0G" +0L" +0O" +0P" +#7000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#8000000 +b1 ! +0# +b1 % +0' +b1 ) +0+ +b1 - +0/ +10 +b1 1 +03 +b1 5 +07 +b1 9 +0; +b1 = +0? +1@ +0C +0H +0M +0R +0W +0\ +0a +0f +b1 i +0k +0o +b1 r +0t +0x +b1 { +0} +0#" +b1 &" +0(" +1)" +0," +b1 /" +01" +05" +b1 8" +0:" +0>" +b1 A" +0C" +0G" +b1 J" +0L" +1M" +0P" +#9000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#10000000 +b10 ! +0# +b10 % +0' +b10 ) +0+ +b10 - +0/ +00 +b10 1 +03 +b10 5 +07 +18 +b10 9 +0; +b10 = +0? +0C +0H +0M +0R +0W +0\ +0a +0f +b10 i +0k +0o +b10 r +0t +0x +b10 { +0} +0#" +b10 &" +0(" +0)" +0," +b10 /" +01" +05" +b10 8" +0:" +1;" +0>" +b10 A" +0C" +0G" +b10 J" +0L" +0P" +#11000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#12000000 +b11 ! +0# +b11 % +0' +b11 ) +0+ +b11 - +0/ +b11 1 +03 +b11 5 +07 +08 +b11 9 +0; +b11 = +0? +0@ +0C +0H +0M +0R +0W +0\ +0a +0f +b11 i +0k +0o +b11 r +0t +0x +b11 { +0} +0#" +b11 &" +0(" +0," +b11 /" +01" +05" +b11 8" +0:" +0;" +0>" +b11 A" +0C" +0G" +b11 J" +0L" +0M" +0P" +#13000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#14000000 +b100 ! +0# +b100 % +0' +b100 ) +0+ +b100 - +0/ +10 +b100 1 +03 +b100 5 +07 +18 +b100 9 +0; +b100 = +0? +0C +0H +0M +0R +0W +0\ +0a +0f +b100 i +0k +0o +b100 r +0t +0x +b100 { +0} +0#" +b100 &" +0(" +1)" +0," +b100 /" +01" +05" +b100 8" +0:" +1;" +0>" +b100 A" +0C" +0G" +b100 J" +0L" +0P" +#15000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#16000000 +b101 ! +0# +b101 % +0' +b101 ) +0+ +1, +b101 - +0/ +b101 1 +03 +b101 5 +07 +b101 9 +0; +1< +b101 = +0? +1@ +0C +0H +0M +0R +0W +0\ +0a +0f +b101 i +0k +0o +b101 r +0t +0x +b101 { +0} +1~ +0#" +b101 &" +0(" +0," +b101 /" +01" +05" +b101 8" +0:" +0>" +b101 A" +0C" +1D" +0G" +b101 J" +0L" +1M" +0P" +#17000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#18000000 +b110 ! +0# +b110 % +0' +b110 ) +0+ +0, +b110 - +0/ +b110 1 +03 +b110 5 +07 +08 +b110 9 +0; +b110 = +0? +0@ +0C +0H +0M +0R +0W +0\ +0a +0f +b110 i +0k +0o +b110 r +0t +0x +b110 { +0} +0~ +0#" +b110 &" +0(" +0," +b110 /" +01" +05" +b110 8" +0:" +0;" +0>" +b110 A" +0C" +0G" +b110 J" +0L" +0M" +0P" +#19000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#20000000 +b111 ! +0# +b111 % +0' +b111 ) +0+ +b111 - +0/ +b111 1 +03 +b111 5 +07 +b111 9 +0; +0< +b111 = +0? +1@ +0C +0H +0M +0R +0W +0\ +0a +0f +b111 i +0k +0o +b111 r +0t +0x +b111 { +0} +0#" +b111 &" +0(" +0," +b111 /" +01" +05" +b111 8" +0:" +0>" +b111 A" +0C" +0D" +0G" +b111 J" +0L" +1M" +0P" +#21000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#22000000 +b1000 ! +0# +b1000 % +0' +b1000 ) +0+ +b1000 - +0/ +00 +b1000 1 +03 +b1000 5 +07 +b1000 9 +0; +b1000 = +0? +0C +0H +0M +0R +0W +0\ +0a +0f +b1000 i +0k +0o +b1000 r +0t +0x +b1000 { +0} +0#" +b1000 &" +0(" +0)" +0," +b1000 /" +01" +05" +b1000 8" +0:" +0>" +b1000 A" +0C" +0G" +b1000 J" +0L" +0P" +#23000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#24000000 +b1001 ! +0# +b1001 % +0' +b1001 ) +0+ +b1001 - +0/ +b1001 1 +03 +b1001 5 +07 +b1001 9 +0; +1< +b1001 = +0? +0@ +0C +0H +0M +0R +0W +0\ +0a +0f +b1001 i +0k +0o +b1001 r +0t +0x +b1001 { +0} +0#" +b1001 &" +0(" +0," +b1001 /" +01" +05" +b1001 8" +0:" +0>" +b1001 A" +0C" +1D" +0G" +b1001 J" +0L" +0M" +0P" +#25000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#26000000 +b1010 ! +0# +b1010 % +0' +b1010 ) +0+ +b1010 - +0/ +b1010 1 +03 +14 +b1010 5 +07 +18 +b1010 9 +0; +b1010 = +0? +0C +0H +0M +0R +0W +0\ +0a +0f +b1010 i +0k +0o +b1010 r +0t +0x +b1010 { +0} +0#" +b1010 &" +0(" +0," +b1010 /" +01" +12" +05" +b1010 8" +0:" +1;" +0>" +b1010 A" +0C" +0G" +b1010 J" +0L" +0P" +#27000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#28000000 +b1011 ! +0# +b1011 % +0' +b1011 ) +0+ +b1011 - +0/ +b1011 1 +03 +04 +b1011 5 +07 +b1011 9 +0; +b1011 = +0? +0C +0H +0M +0R +0W +0\ +0a +0f +b1011 i +0k +0o +b1011 r +0t +0x +b1011 { +0} +0#" +b1011 &" +0(" +0," +b1011 /" +01" +02" +05" +b1011 8" +0:" +0>" +b1011 A" +0C" +0G" +b1011 J" +0L" +0P" +#29000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#30000000 +b1100 ! +0# +b1100 % +0' +b1100 ) +0+ +b1100 - +0/ +b1100 1 +03 +b1100 5 +07 +08 +b1100 9 +0; +b1100 = +0? +0C +0H +0M +0R +0W +0\ +0a +0f +b1100 i +0k +0o +b1100 r +0t +0x +b1100 { +0} +0#" +b1100 &" +0(" +0," +b1100 /" +01" +05" +b1100 8" +0:" +0;" +0>" +b1100 A" +0C" +0G" +b1100 J" +0L" +0P" +#31000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#32000000 +b1101 ! +0# +b1101 % +0' +b1101 ) +0+ +b1101 - +0/ +b1101 1 +03 +b1101 5 +07 +b1101 9 +0; +0< +b1101 = +0? +0C +0H +0M +0R +0W +0\ +0a +0f +b1101 i +0k +0o +b1101 r +0t +0x +b1101 { +0} +0#" +b1101 &" +0(" +0," +b1101 /" +01" +05" +b1101 8" +0:" +0>" +b1101 A" +0C" +0D" +0G" +b1101 J" +0L" +0P" +#33000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#34000000 +b1110 ! +0# +b1110 % +0' +b1110 ) +0+ +b1110 - +0/ +b1110 1 +03 +b1110 5 +07 +b1110 9 +0; +b1110 = +0? +1@ +0C +0H +0M +0R +0W +0\ +0a +0f +b1110 i +0k +0o +b1110 r +0t +0x +b1110 { +0} +0#" +b1110 &" +0(" +0," +b1110 /" +01" +05" +b1110 8" +0:" +0>" +b1110 A" +0C" +0G" +b1110 J" +0L" +1M" +0P" +#35000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#36000000 +b1111 ! +0# +b1111 % +0' +b1111 ) +0+ +b1111 - +0/ +b1111 1 +03 +b1111 5 +07 +b1111 9 +0; +b1111 = +0? +0@ +0C +0H +0M +0R +0W +0\ +0a +0f +b1111 i +0k +0o +b1111 r +0t +0x +b1111 { +0} +0#" +b1111 &" +0(" +0," +b1111 /" +01" +05" +b1111 8" +0:" +0>" +b1111 A" +0C" +0G" +b1111 J" +0L" +0M" +0P" +#37000000 +1# +1' +1+ +1/ +13 +17 +1; +1? +1C +1H +1M +1R +1W +1\ +1a +1f +1k +1o +1t +1x +1} +1#" +1(" +1," +11" +15" +1:" +1>" +1C" +1G" +1L" +1P" +#38000000 +0# +0' +0+ +0/ +03 +07 +0; +0? +0C +0H +0M +0R +0W +0\ +0a +0f +0k +0o +0t +0x +0} +0#" +0(" +0," +01" +05" +0:" +0>" +0C" +0G" +0L" +0P" diff --git a/crates/fayalite/tests/sim/expected/memories.txt b/crates/fayalite/tests/sim/expected/memories.txt index f7f88e3b..0358bb31 100644 --- a/crates/fayalite/tests/sim/expected/memories.txt +++ b/crates/fayalite/tests/sim/expected/memories.txt @@ -719,9 +719,9 @@ Simulation { }.w.mask.1, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "memories", children: [ @@ -1616,10 +1616,15 @@ Simulation { }, ), ], - instant: 22 μs, clocks_triggered: [ StatePartIndex(1), StatePartIndex(6), ], + event_queue: EventQueue(EventQueueData { + instant: 22 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/memories.vcd b/crates/fayalite/tests/sim/expected/memories.vcd index bedc354b..d8f58172 100644 --- a/crates/fayalite/tests/sim/expected/memories.vcd +++ b/crates/fayalite/tests/sim/expected/memories.vcd @@ -234,13 +234,13 @@ b100000 6 b10000 9 b100000 I 1# -1( -1/ -14 b10000 $ b100000 % +1( +1/ b10000 0 b100000 1 +14 #4000000 0# 0( @@ -256,11 +256,11 @@ b1000000 6 b10000 9 b1000000 I 1# +b1000000 % 1( 1/ -14 -b1000000 % b1000000 1 +14 #6000000 0# 0( @@ -278,11 +278,11 @@ b1100000 6 b1010000 9 b1000000 I 1# +b1010000 $ 1( 1/ -14 -b1010000 $ b1010000 0 +14 #8000000 0# 0( diff --git a/crates/fayalite/tests/sim/expected/memories2.txt b/crates/fayalite/tests/sim/expected/memories2.txt index c216104c..b4041ba1 100644 --- a/crates/fayalite/tests/sim/expected/memories2.txt +++ b/crates/fayalite/tests/sim/expected/memories2.txt @@ -677,9 +677,9 @@ Simulation { }.rw.wmode, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "memories2", children: [ @@ -1260,9 +1260,14 @@ Simulation { }, ), ], - instant: 22 μs, clocks_triggered: [ StatePartIndex(3), ], + event_queue: EventQueue(EventQueueData { + instant: 22 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/memories2.vcd b/crates/fayalite/tests/sim/expected/memories2.vcd index 4039754f..0ac20f19 100644 --- a/crates/fayalite/tests/sim/expected/memories2.vcd +++ b/crates/fayalite/tests/sim/expected/memories2.vcd @@ -100,8 +100,8 @@ $end 1) #1250000 1# -1* b11 $ +1* sHdlSome\x20(1) + 1, #1500000 @@ -113,8 +113,8 @@ sHdlSome\x20(1) + 0) #2250000 1# -1* b0 $ +1* sHdlNone\x20(0) + 0, #2500000 @@ -303,8 +303,8 @@ b11 ! b11 ( #17250000 1# -1* b11 $ +1* sHdlSome\x20(1) + 1, #17500000 @@ -316,8 +316,8 @@ b10 ! b10 ( #18250000 1# -1* b0 $ +1* sHdlNone\x20(0) + 0, #18500000 @@ -339,8 +339,8 @@ b1 ! b1 ( #20250000 1# -1* b1 $ +1* sHdlSome\x20(1) + #20500000 #20750000 @@ -353,8 +353,8 @@ b0 ( 0) #21250000 1# -1* b0 $ +1* sHdlNone\x20(0) + #21500000 #21750000 diff --git a/crates/fayalite/tests/sim/expected/memories3.txt b/crates/fayalite/tests/sim/expected/memories3.txt index 8114c7e4..2213912c 100644 --- a/crates/fayalite/tests/sim/expected/memories3.txt +++ b/crates/fayalite/tests/sim/expected/memories3.txt @@ -1761,9 +1761,9 @@ Simulation { }.w.mask[7], }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "memories3", children: [ @@ -3275,10 +3275,15 @@ Simulation { }, ), ], - instant: 15 μs, clocks_triggered: [ StatePartIndex(1), StatePartIndex(6), ], + event_queue: EventQueue(EventQueueData { + instant: 15 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/memories3.vcd b/crates/fayalite/tests/sim/expected/memories3.vcd index 5768560e..32ee75e6 100644 --- a/crates/fayalite/tests/sim/expected/memories3.vcd +++ b/crates/fayalite/tests/sim/expected/memories3.vcd @@ -420,6 +420,10 @@ b10000 T 1\ #3250000 1# +b110100 % +b1111000 ' +b10011010 ( +b11110000 + 1. 1A b110100 C @@ -427,10 +431,6 @@ b1111000 E b10011010 F b11110000 I 1L -b110100 % -b1111000 ' -b10011010 ( -b11110000 + #3500000 #3750000 0# @@ -508,6 +508,14 @@ b1010100 '" b110010 /" b10000 7" 1# +b11111110 $ +b11011100 % +b10111010 & +b10011000 ' +b1110110 ( +b1010100 ) +b110010 * +b10000 + 1. 1A b11111110 B @@ -519,14 +527,6 @@ b1010100 G b110010 H b10000 I 1L -b11111110 $ -b11011100 % -b10111010 & -b10011000 ' -b1110110 ( -b1010100 ) -b110010 * -b10000 + #6500000 #6750000 0# @@ -562,6 +562,14 @@ b1000110 (" b10001010 0" b11001110 8" 1# +b0 $ +b0 % +b0 & +b0 ' +b0 ( +b0 ) +b0 * +b0 + 1. 1A b0 B @@ -573,14 +581,6 @@ b0 G b0 H b0 I 1L -b0 $ -b0 % -b0 & -b0 ' -b0 ( -b0 ) -b0 * -b0 + #7500000 #7750000 0# @@ -688,6 +688,14 @@ b1 ! b1 ? #10250000 1# +b11111110 $ +b11011100 % +b10111010 & +b10011000 ' +b1110110 ( +b1010100 ) +b110010 * +b10000 + 1. 1A b11111110 B @@ -699,14 +707,6 @@ b1010100 G b110010 H b10000 I 1L -b11111110 $ -b11011100 % -b10111010 & -b10011000 ' -b1110110 ( -b1010100 ) -b110010 * -b10000 + #10500000 #10750000 0# @@ -718,6 +718,14 @@ b10 ! b10 ? #11250000 1# +b10011 $ +b1010111 % +b10011011 & +b11011111 ' +b10 ( +b1000110 ) +b10001010 * +b11001110 + 1. 1A b10011 B @@ -729,14 +737,6 @@ b1000110 G b10001010 H b11001110 I 1L -b10011 $ -b1010111 % -b10011011 & -b11011111 ' -b10 ( -b1000110 ) -b10001010 * -b11001110 + #11500000 #11750000 0# @@ -748,6 +748,14 @@ b11 ! b11 ? #12250000 1# +b1110100 $ +b1100101 % +b1110011 & +b1110100 ' +b1101001 ( +b1101110 ) +b1100111 * +b100001 + 1. 1A b1110100 B @@ -759,14 +767,6 @@ b1101110 G b1100111 H b100001 I 1L -b1110100 $ -b1100101 % -b1110011 & -b1110100 ' -b1101001 ( -b1101110 ) -b1100111 * -b100001 + #12500000 #12750000 0# @@ -780,6 +780,14 @@ b0 ? 0@ #13250000 1# +b1101101 $ +b1101111 % +b1110010 & +b1100101 ' +b100000 ( +b1110100 ) +b1110011 * +b1110100 + 1. 1A b1101101 B @@ -791,14 +799,6 @@ b1110100 G b1110011 H b1110100 I 1L -b1101101 $ -b1101111 % -b1110010 & -b1100101 ' -b100000 ( -b1110100 ) -b1110011 * -b1110100 + #13500000 #13750000 0# @@ -808,6 +808,14 @@ b1110100 + #14000000 #14250000 1# +b0 $ +b0 % +b0 & +b0 ' +b0 ( +b0 ) +b0 * +b0 + 1. 1A b0 B @@ -819,14 +827,6 @@ b0 G b0 H b0 I 1L -b0 $ -b0 % -b0 & -b0 ' -b0 ( -b0 ) -b0 * -b0 + #14500000 #14750000 0# diff --git a/crates/fayalite/tests/sim/expected/mod1.txt b/crates/fayalite/tests/sim/expected/mod1.txt index 4ef02b2e..3f7a55e5 100644 --- a/crates/fayalite/tests/sim/expected/mod1.txt +++ b/crates/fayalite/tests/sim/expected/mod1.txt @@ -274,9 +274,9 @@ Simulation { }.o.o2, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "mod1", children: [ @@ -558,7 +558,12 @@ Simulation { }, ), ], - instant: 2 μs, clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 2 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/phantom_const.txt b/crates/fayalite/tests/sim/expected/phantom_const.txt new file mode 100644 index 00000000..94072ac6 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/phantom_const.txt @@ -0,0 +1,525 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 5, + debug_data: [ + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<0>, + }, + ], + .. + }, + big_slots: StatePartLayout { + len: 8, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr", + ty: UInt<0>, + }, + SlotDebugData { + name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.clk", + ty: Clock, + }, + SlotDebugData { + name: "", + ty: UInt<8>, + }, + SlotDebugData { + name: "", + ty: UInt<0>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 1, + debug_data: [ + (), + ], + layout_data: [ + MemoryData { + array_type: Array, + data: [ + // len = 0x1 + [0x0]: 0x0, + ], + }, + ], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Const { + dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 1: Copy { + dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", ty: Bool }, + src: StatePartIndex(7), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 2: Const { + dest: StatePartIndex(5), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + value: 0x0, + }, + 3: Copy { + dest: StatePartIndex(6), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(5), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 4: Copy { + dest: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.clk", ty: Clock }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 5: Const { + dest: StatePartIndex(3), // (0x0) SlotDebugData { name: "", ty: UInt<8> }, + value: 0x0, + }, + 6: CastToUInt { + dest: StatePartIndex(4), // (0x0) SlotDebugData { name: "", ty: UInt<0> }, + src: StatePartIndex(3), // (0x0) SlotDebugData { name: "", ty: UInt<8> }, + dest_width: 0, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 7: Copy { + dest: StatePartIndex(0), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr", ty: UInt<0> }, + src: StatePartIndex(4), // (0x0) SlotDebugData { name: "", ty: UInt<0> }, + }, + // at: module-XXXXXXXXXX.rs:3:1 + 8: CastBigToArrayIndex { + dest: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: UInt<0> }, + src: StatePartIndex(0), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr", ty: UInt<0> }, + }, + 9: IsNonZeroDestIsSmall { + dest: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", ty: Bool }, + }, + 10: BranchIfSmallZero { + target: 12, + value: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 11: Branch { + target: 12, + }, + 12: IsNonZeroDestIsSmall { + dest: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.clk", ty: Clock }, + }, + 13: AndSmall { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(0), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 14: BranchIfSmallZero { + target: 15, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 15: XorSmallImmediate { + dest: StatePartIndex(0), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 16: Return, + ], + .. + }, + pc: 16, + memory_write_log: [], + memories: StatePart { + value: [ + MemoryData { + array_type: Array, + data: [ + // len = 0x1 + [0x0]: 0x0, + ], + }, + ], + }, + small_slots: StatePart { + value: [ + 1, + 0, + 0, + 0, + 0, + ], + }, + big_slots: StatePart { + value: [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::phantom_const, + instantiated: Module { + name: phantom_const, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::phantom_const, + instantiated: Module { + name: phantom_const, + .. + }, + }.out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::phantom_const, + instantiated: Module { + name: phantom_const, + .. + }, + }.out, + Instance { + name: ::phantom_const, + instantiated: Module { + name: phantom_const, + .. + }, + }.out[0], + Instance { + name: ::phantom_const, + instantiated: Module { + name: phantom_const, + .. + }, + }.out[1], + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [], + trace_decls: TraceModule { + name: "phantom_const", + children: [ + TraceModuleIO { + name: "out", + child: TraceArray { + name: "out", + elements: [ + TracePhantomConst { + location: TraceScalarId(0), + name: "[0]", + ty: PhantomConst( + ["a","b"], + ), + flow: Sink, + }, + TracePhantomConst { + location: TraceScalarId(1), + name: "[1]", + ty: PhantomConst( + ["a","b"], + ), + flow: Sink, + }, + ], + ty: Array, + flow: Sink, + }, + ty: Array, + flow: Sink, + }, + TraceMem { + id: TraceMemoryId(0), + name: "mem", + stride: 0, + element_type: TracePhantomConst { + location: TraceMemoryLocation { + id: TraceMemoryId(0), + depth: 1, + stride: 0, + start: 0, + len: 0, + }, + name: "mem", + ty: PhantomConst( + "mem_element", + ), + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(2), + name: "addr", + ty: UInt<0>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(3), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(4), + name: "clk", + flow: Sink, + }, + TracePhantomConst { + location: TraceScalarId(5), + name: "data", + ty: PhantomConst( + "mem_element", + ), + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<0>, + /* offset = 0 */ + en: Bool, + /* offset = 1 */ + clk: Clock, + #[hdl(flip)] /* offset = 2 */ + data: PhantomConst( + "mem_element", + ), + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<0>, + /* offset = 0 */ + en: Bool, + /* offset = 1 */ + clk: Clock, + #[hdl(flip)] /* offset = 2 */ + data: PhantomConst( + "mem_element", + ), + }, + }, + ], + array_type: Array, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: PhantomConst { + ty: PhantomConst( + ["a","b"], + ), + }, + state: PhantomConst, + last_state: PhantomConst, + }, + SimTrace { + id: TraceScalarId(1), + kind: PhantomConst { + ty: PhantomConst( + ["a","b"], + ), + }, + state: PhantomConst, + last_state: PhantomConst, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(0), + ty: UInt<0>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigBool { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigClock { + index: StatePartIndex(2), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(5), + kind: PhantomConst { + ty: PhantomConst( + "mem_element", + ), + }, + state: PhantomConst, + last_state: PhantomConst, + }, + ], + trace_memories: { + StatePartIndex(0): TraceMem { + id: TraceMemoryId(0), + name: "mem", + stride: 0, + element_type: TracePhantomConst { + location: TraceMemoryLocation { + id: TraceMemoryId(0), + depth: 1, + stride: 0, + start: 0, + len: 0, + }, + name: "mem", + ty: PhantomConst( + "mem_element", + ), + flow: Duplex, + }, + ports: [ + TraceMemPort { + name: "r0", + bundle: TraceBundle { + name: "r0", + fields: [ + TraceUInt { + location: TraceScalarId(2), + name: "addr", + ty: UInt<0>, + flow: Sink, + }, + TraceBool { + location: TraceScalarId(3), + name: "en", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(4), + name: "clk", + flow: Sink, + }, + TracePhantomConst { + location: TraceScalarId(5), + name: "data", + ty: PhantomConst( + "mem_element", + ), + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + addr: UInt<0>, + /* offset = 0 */ + en: Bool, + /* offset = 1 */ + clk: Clock, + #[hdl(flip)] /* offset = 2 */ + data: PhantomConst( + "mem_element", + ), + }, + flow: Sink, + }, + ty: Bundle { + /* offset = 0 */ + addr: UInt<0>, + /* offset = 0 */ + en: Bool, + /* offset = 1 */ + clk: Clock, + #[hdl(flip)] /* offset = 2 */ + data: PhantomConst( + "mem_element", + ), + }, + }, + ], + array_type: Array, + }, + }, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [ + StatePartIndex(1), + ], + event_queue: EventQueue(EventQueueData { + instant: 1 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/phantom_const.vcd b/crates/fayalite/tests/sim/expected/phantom_const.vcd new file mode 100644 index 00000000..ba3869b8 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/phantom_const.vcd @@ -0,0 +1,31 @@ +$timescale 1 ps $end +$scope module phantom_const $end +$scope struct out $end +$var string 1 ! \[0] $end +$var string 1 " \[1] $end +$upscope $end +$scope struct mem $end +$scope struct contents $end +$scope struct \[0] $end +$var string 1 ' mem $end +$upscope $end +$upscope $end +$scope struct r0 $end +$var string 0 # addr $end +$var wire 1 $ en $end +$var wire 1 % clk $end +$var string 1 & data $end +$upscope $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +s0 ' +sPhantomConst([\"a\",\"b\"]) ! +sPhantomConst([\"a\",\"b\"]) " +s0 # +0$ +0% +sPhantomConst(\"mem_element\") & +$end +#1000000 diff --git a/crates/fayalite/tests/sim/expected/ripple_counter.txt b/crates/fayalite/tests/sim/expected/ripple_counter.txt index 9e4e0d1b..9cc5f021 100644 --- a/crates/fayalite/tests/sim/expected/ripple_counter.txt +++ b/crates/fayalite/tests/sim/expected/ripple_counter.txt @@ -162,7 +162,7 @@ Simulation { }, SlotDebugData { name: "", - ty: Bool, + ty: UInt<1>, }, SlotDebugData { name: "", @@ -429,12 +429,12 @@ Simulation { }, // at: module-XXXXXXXXXX.rs:1:1 26: Const { - dest: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, value: 0x0, }, 27: Copy { dest: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, - src: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, }, 28: Copy { dest: StatePartIndex(26), // (0x1) SlotDebugData { name: ".clk", ty: Clock }, @@ -743,6 +743,7 @@ Simulation { }.o, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [ SimulationExternModuleState { @@ -777,6 +778,7 @@ Simulation { }, }, did_initial_settle: true, + clocks_for_past: {}, }, sim: ExternModuleSimulation { generator: SimGeneratorFn { @@ -827,52 +829,6 @@ Simulation { 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 { @@ -906,6 +862,7 @@ Simulation { }, }, did_initial_settle: true, + clocks_for_past: {}, }, sim: ExternModuleSimulation { generator: SimGeneratorFn { @@ -956,52 +913,6 @@ Simulation { 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 { @@ -1035,6 +946,7 @@ Simulation { }, }, did_initial_settle: true, + clocks_for_past: {}, }, sim: ExternModuleSimulation { generator: SimGeneratorFn { @@ -1085,55 +997,8 @@ Simulation { 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: [ @@ -1593,11 +1458,315 @@ Simulation { }, ), ], - instant: 256 μs, clocks_triggered: [ StatePartIndex(1), StatePartIndex(4), StatePartIndex(7), ], + event_queue: EventQueue(EventQueueData { + instant: 256 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 152, + values: { + 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, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + SensitivitySet { + id: 167, + values: { + 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, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + SensitivitySet { + id: 170, + values: { + 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, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + 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, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 170, + .. + }, + }, + ), + 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, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 167, + .. + }, + }, + ), + 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, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 152, + .. + }, + }, + ), + }, .. } \ 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 index 6f14a8e8..550205f8 100644 --- a/crates/fayalite/tests/sim/expected/ripple_counter.vcd +++ b/crates/fayalite/tests/sim/expected/ripple_counter.vcd @@ -66,1688 +66,1648 @@ b0 " $end #1000000 1! -1) b1 " 1# +1) 1* 1, -1+ -b11 " +b111 " 1$ +1% +1+ 1- 1. -b111 " -1% 1/ 11 -10 -b1111 " +b11111 " 1& +1' +10 12 13 -b11111 " -1' 14 16 -15 b111111 " 1( +15 17 #2000000 0! #3000000 1! -0) b111110 " 0# +0) 0* 0, #4000000 0! #5000000 1! -1) b111111 " 1# +1) 1* 1, -0+ b111101 " 0$ +0+ 0- #6000000 0! #7000000 1! -0) b111100 " 0# +0) 0* 0, #8000000 0! #9000000 1! -1) b111101 " 1# +1) 1* 1, -1+ -b111111 " +b111011 " 1$ +0% +1+ 1- 0. -b111011 " -0% 0/ 01 #10000000 0! #11000000 1! -0) b111010 " 0# +0) 0* 0, #12000000 0! #13000000 1! -1) b111011 " 1# +1) 1* 1, -0+ b111001 " 0$ +0+ 0- #14000000 0! #15000000 1! -0) b111000 " 0# +0) 0* 0, #16000000 0! #17000000 1! -1) b111001 " 1# +1) 1* 1, -1+ -b111011 " +b111111 " 1$ +1% +1+ 1- 1. -b111111 " -1% 1/ 11 -00 b110111 " 0& +00 02 #18000000 0! #19000000 1! -0) b110110 " 0# +0) 0* 0, #20000000 0! #21000000 1! -1) b110111 " 1# +1) 1* 1, -0+ b110101 " 0$ +0+ 0- #22000000 0! #23000000 1! -0) b110100 " 0# +0) 0* 0, #24000000 0! #25000000 1! -1) b110101 " 1# +1) 1* 1, -1+ -b110111 " +b110011 " 1$ +0% +1+ 1- 0. -b110011 " -0% 0/ 01 #26000000 0! #27000000 1! -0) b110010 " 0# +0) 0* 0, #28000000 0! #29000000 1! -1) b110011 " 1# +1) 1* 1, -0+ b110001 " 0$ +0+ 0- #30000000 0! #31000000 1! -0) b110000 " 0# +0) 0* 0, #32000000 0! #33000000 1! -1) b110001 " 1# +1) 1* 1, -1+ -b110011 " +b110111 " 1$ +1% +1+ 1- 1. -b110111 " -1% 1/ 11 -10 -b111111 " +b101111 " 1& +0' +10 12 03 -b101111 " -0' 04 06 #34000000 0! #35000000 1! -0) b101110 " 0# +0) 0* 0, #36000000 0! #37000000 1! -1) b101111 " 1# +1) 1* 1, -0+ b101101 " 0$ +0+ 0- #38000000 0! #39000000 1! -0) b101100 " 0# +0) 0* 0, #40000000 0! #41000000 1! -1) b101101 " 1# +1) 1* 1, -1+ -b101111 " +b101011 " 1$ +0% +1+ 1- 0. -b101011 " -0% 0/ 01 #42000000 0! #43000000 1! -0) b101010 " 0# +0) 0* 0, #44000000 0! #45000000 1! -1) b101011 " 1# +1) 1* 1, -0+ b101001 " 0$ +0+ 0- #46000000 0! #47000000 1! -0) b101000 " 0# +0) 0* 0, #48000000 0! #49000000 1! -1) b101001 " 1# +1) 1* 1, -1+ -b101011 " +b101111 " 1$ +1% +1+ 1- 1. -b101111 " -1% 1/ 11 -00 b100111 " 0& +00 02 #50000000 0! #51000000 1! -0) b100110 " 0# +0) 0* 0, #52000000 0! #53000000 1! -1) b100111 " 1# +1) 1* 1, -0+ b100101 " 0$ +0+ 0- #54000000 0! #55000000 1! -0) b100100 " 0# +0) 0* 0, #56000000 0! #57000000 1! -1) b100101 " 1# +1) 1* 1, -1+ -b100111 " +b100011 " 1$ +0% +1+ 1- 0. -b100011 " -0% 0/ 01 #58000000 0! #59000000 1! -0) b100010 " 0# +0) 0* 0, #60000000 0! #61000000 1! -1) b100011 " 1# +1) 1* 1, -0+ b100001 " 0$ +0+ 0- #62000000 0! #63000000 1! -0) b100000 " 0# +0) 0* 0, #64000000 0! #65000000 1! -1) b100001 " 1# +1) 1* 1, -1+ -b100011 " +b100111 " 1$ +1% +1+ 1- 1. -b100111 " -1% 1/ 11 -10 -b101111 " +b111111 " 1& +1' +10 12 13 -b111111 " -1' 14 16 -05 b11111 " 0( +05 07 #66000000 0! #67000000 1! -0) b11110 " 0# +0) 0* 0, #68000000 0! #69000000 1! -1) b11111 " 1# +1) 1* 1, -0+ b11101 " 0$ +0+ 0- #70000000 0! #71000000 1! -0) b11100 " 0# +0) 0* 0, #72000000 0! #73000000 1! -1) b11101 " 1# +1) 1* 1, -1+ -b11111 " +b11011 " 1$ +0% +1+ 1- 0. -b11011 " -0% 0/ 01 #74000000 0! #75000000 1! -0) b11010 " 0# +0) 0* 0, #76000000 0! #77000000 1! -1) b11011 " 1# +1) 1* 1, -0+ b11001 " 0$ +0+ 0- #78000000 0! #79000000 1! -0) b11000 " 0# +0) 0* 0, #80000000 0! #81000000 1! -1) b11001 " 1# +1) 1* 1, -1+ -b11011 " +b11111 " 1$ +1% +1+ 1- 1. -b11111 " -1% 1/ 11 -00 b10111 " 0& +00 02 #82000000 0! #83000000 1! -0) b10110 " 0# +0) 0* 0, #84000000 0! #85000000 1! -1) b10111 " 1# +1) 1* 1, -0+ b10101 " 0$ +0+ 0- #86000000 0! #87000000 1! -0) b10100 " 0# +0) 0* 0, #88000000 0! #89000000 1! -1) b10101 " 1# +1) 1* 1, -1+ -b10111 " +b10011 " 1$ +0% +1+ 1- 0. -b10011 " -0% 0/ 01 #90000000 0! #91000000 1! -0) b10010 " 0# +0) 0* 0, #92000000 0! #93000000 1! -1) b10011 " 1# +1) 1* 1, -0+ b10001 " 0$ +0+ 0- #94000000 0! #95000000 1! -0) b10000 " 0# +0) 0* 0, #96000000 0! #97000000 1! -1) b10001 " 1# +1) 1* 1, -1+ -b10011 " +b10111 " 1$ +1% +1+ 1- 1. -b10111 " -1% 1/ 11 -10 -b11111 " +b1111 " 1& +0' +10 12 03 -b1111 " -0' 04 06 #98000000 0! #99000000 1! -0) b1110 " 0# +0) 0* 0, #100000000 0! #101000000 1! -1) b1111 " 1# +1) 1* 1, -0+ b1101 " 0$ +0+ 0- #102000000 0! #103000000 1! -0) b1100 " 0# +0) 0* 0, #104000000 0! #105000000 1! -1) b1101 " 1# +1) 1* 1, -1+ -b1111 " +b1011 " 1$ +0% +1+ 1- 0. -b1011 " -0% 0/ 01 #106000000 0! #107000000 1! -0) b1010 " 0# +0) 0* 0, #108000000 0! #109000000 1! -1) b1011 " 1# +1) 1* 1, -0+ b1001 " 0$ +0+ 0- #110000000 0! #111000000 1! -0) b1000 " 0# +0) 0* 0, #112000000 0! #113000000 1! -1) b1001 " 1# +1) 1* 1, -1+ -b1011 " +b1111 " 1$ +1% +1+ 1- 1. -b1111 " -1% 1/ 11 -00 b111 " 0& +00 02 #114000000 0! #115000000 1! -0) b110 " 0# +0) 0* 0, #116000000 0! #117000000 1! -1) b111 " 1# +1) 1* 1, -0+ b101 " 0$ +0+ 0- #118000000 0! #119000000 1! -0) b100 " 0# +0) 0* 0, #120000000 0! #121000000 1! -1) b101 " 1# +1) 1* 1, -1+ -b111 " +b11 " 1$ +0% +1+ 1- 0. -b11 " -0% 0/ 01 #122000000 0! #123000000 1! -0) b10 " 0# +0) 0* 0, #124000000 0! #125000000 1! -1) b11 " 1# +1) 1* 1, -0+ b1 " 0$ +0+ 0- #126000000 0! #127000000 1! -0) b0 " 0# +0) 0* 0, #128000000 0! #129000000 1! -1) b1 " 1# +1) 1* 1, -1+ -b11 " +b111 " 1$ +1% +1+ 1- 1. -b111 " -1% 1/ 11 -10 -b1111 " +b11111 " 1& +1' +10 12 13 -b11111 " -1' 14 16 -15 b111111 " 1( +15 17 #130000000 0! #131000000 1! -0) b111110 " 0# +0) 0* 0, #132000000 0! #133000000 1! -1) b111111 " 1# +1) 1* 1, -0+ b111101 " 0$ +0+ 0- #134000000 0! #135000000 1! -0) b111100 " 0# +0) 0* 0, #136000000 0! #137000000 1! -1) b111101 " 1# +1) 1* 1, -1+ -b111111 " +b111011 " 1$ +0% +1+ 1- 0. -b111011 " -0% 0/ 01 #138000000 0! #139000000 1! -0) b111010 " 0# +0) 0* 0, #140000000 0! #141000000 1! -1) b111011 " 1# +1) 1* 1, -0+ b111001 " 0$ +0+ 0- #142000000 0! #143000000 1! -0) b111000 " 0# +0) 0* 0, #144000000 0! #145000000 1! -1) b111001 " 1# +1) 1* 1, -1+ -b111011 " +b111111 " 1$ +1% +1+ 1- 1. -b111111 " -1% 1/ 11 -00 b110111 " 0& +00 02 #146000000 0! #147000000 1! -0) b110110 " 0# +0) 0* 0, #148000000 0! #149000000 1! -1) b110111 " 1# +1) 1* 1, -0+ b110101 " 0$ +0+ 0- #150000000 0! #151000000 1! -0) b110100 " 0# +0) 0* 0, #152000000 0! #153000000 1! -1) b110101 " 1# +1) 1* 1, -1+ -b110111 " +b110011 " 1$ +0% +1+ 1- 0. -b110011 " -0% 0/ 01 #154000000 0! #155000000 1! -0) b110010 " 0# +0) 0* 0, #156000000 0! #157000000 1! -1) b110011 " 1# +1) 1* 1, -0+ b110001 " 0$ +0+ 0- #158000000 0! #159000000 1! -0) b110000 " 0# +0) 0* 0, #160000000 0! #161000000 1! -1) b110001 " 1# +1) 1* 1, -1+ -b110011 " +b110111 " 1$ +1% +1+ 1- 1. -b110111 " -1% 1/ 11 -10 -b111111 " +b101111 " 1& +0' +10 12 03 -b101111 " -0' 04 06 #162000000 0! #163000000 1! -0) b101110 " 0# +0) 0* 0, #164000000 0! #165000000 1! -1) b101111 " 1# +1) 1* 1, -0+ b101101 " 0$ +0+ 0- #166000000 0! #167000000 1! -0) b101100 " 0# +0) 0* 0, #168000000 0! #169000000 1! -1) b101101 " 1# +1) 1* 1, -1+ -b101111 " +b101011 " 1$ +0% +1+ 1- 0. -b101011 " -0% 0/ 01 #170000000 0! #171000000 1! -0) b101010 " 0# +0) 0* 0, #172000000 0! #173000000 1! -1) b101011 " 1# +1) 1* 1, -0+ b101001 " 0$ +0+ 0- #174000000 0! #175000000 1! -0) b101000 " 0# +0) 0* 0, #176000000 0! #177000000 1! -1) b101001 " 1# +1) 1* 1, -1+ -b101011 " +b101111 " 1$ +1% +1+ 1- 1. -b101111 " -1% 1/ 11 -00 b100111 " 0& +00 02 #178000000 0! #179000000 1! -0) b100110 " 0# +0) 0* 0, #180000000 0! #181000000 1! -1) b100111 " 1# +1) 1* 1, -0+ b100101 " 0$ +0+ 0- #182000000 0! #183000000 1! -0) b100100 " 0# +0) 0* 0, #184000000 0! #185000000 1! -1) b100101 " 1# +1) 1* 1, -1+ -b100111 " +b100011 " 1$ +0% +1+ 1- 0. -b100011 " -0% 0/ 01 #186000000 0! #187000000 1! -0) b100010 " 0# +0) 0* 0, #188000000 0! #189000000 1! -1) b100011 " 1# +1) 1* 1, -0+ b100001 " 0$ +0+ 0- #190000000 0! #191000000 1! -0) b100000 " 0# +0) 0* 0, #192000000 0! #193000000 1! -1) b100001 " 1# +1) 1* 1, -1+ -b100011 " +b100111 " 1$ +1% +1+ 1- 1. -b100111 " -1% 1/ 11 -10 -b101111 " +b111111 " 1& +1' +10 12 13 -b111111 " -1' 14 16 -05 b11111 " 0( +05 07 #194000000 0! #195000000 1! -0) b11110 " 0# +0) 0* 0, #196000000 0! #197000000 1! -1) b11111 " 1# +1) 1* 1, -0+ b11101 " 0$ +0+ 0- #198000000 0! #199000000 1! -0) b11100 " 0# +0) 0* 0, #200000000 0! #201000000 1! -1) b11101 " 1# +1) 1* 1, -1+ -b11111 " +b11011 " 1$ +0% +1+ 1- 0. -b11011 " -0% 0/ 01 #202000000 0! #203000000 1! -0) b11010 " 0# +0) 0* 0, #204000000 0! #205000000 1! -1) b11011 " 1# +1) 1* 1, -0+ b11001 " 0$ +0+ 0- #206000000 0! #207000000 1! -0) b11000 " 0# +0) 0* 0, #208000000 0! #209000000 1! -1) b11001 " 1# +1) 1* 1, -1+ -b11011 " +b11111 " 1$ +1% +1+ 1- 1. -b11111 " -1% 1/ 11 -00 b10111 " 0& +00 02 #210000000 0! #211000000 1! -0) b10110 " 0# +0) 0* 0, #212000000 0! #213000000 1! -1) b10111 " 1# +1) 1* 1, -0+ b10101 " 0$ +0+ 0- #214000000 0! #215000000 1! -0) b10100 " 0# +0) 0* 0, #216000000 0! #217000000 1! -1) b10101 " 1# +1) 1* 1, -1+ -b10111 " +b10011 " 1$ +0% +1+ 1- 0. -b10011 " -0% 0/ 01 #218000000 0! #219000000 1! -0) b10010 " 0# +0) 0* 0, #220000000 0! #221000000 1! -1) b10011 " 1# +1) 1* 1, -0+ b10001 " 0$ +0+ 0- #222000000 0! #223000000 1! -0) b10000 " 0# +0) 0* 0, #224000000 0! #225000000 1! -1) b10001 " 1# +1) 1* 1, -1+ -b10011 " +b10111 " 1$ +1% +1+ 1- 1. -b10111 " -1% 1/ 11 -10 -b11111 " +b1111 " 1& +0' +10 12 03 -b1111 " -0' 04 06 #226000000 0! #227000000 1! -0) b1110 " 0# +0) 0* 0, #228000000 0! #229000000 1! -1) b1111 " 1# +1) 1* 1, -0+ b1101 " 0$ +0+ 0- #230000000 0! #231000000 1! -0) b1100 " 0# +0) 0* 0, #232000000 0! #233000000 1! -1) b1101 " 1# +1) 1* 1, -1+ -b1111 " +b1011 " 1$ +0% +1+ 1- 0. -b1011 " -0% 0/ 01 #234000000 0! #235000000 1! -0) b1010 " 0# +0) 0* 0, #236000000 0! #237000000 1! -1) b1011 " 1# +1) 1* 1, -0+ b1001 " 0$ +0+ 0- #238000000 0! #239000000 1! -0) b1000 " 0# +0) 0* 0, #240000000 0! #241000000 1! -1) b1001 " 1# +1) 1* 1, -1+ -b1011 " +b1111 " 1$ +1% +1+ 1- 1. -b1111 " -1% 1/ 11 -00 b111 " 0& +00 02 #242000000 0! #243000000 1! -0) b110 " 0# +0) 0* 0, #244000000 0! #245000000 1! -1) b111 " 1# +1) 1* 1, -0+ b101 " 0$ +0+ 0- #246000000 0! #247000000 1! -0) b100 " 0# +0) 0* 0, #248000000 0! #249000000 1! -1) b101 " 1# +1) 1* 1, -1+ -b111 " +b11 " 1$ +0% +1+ 1- 0. -b11 " -0% 0/ 01 #250000000 0! #251000000 1! -0) b10 " 0# +0) 0* 0, #252000000 0! #253000000 1! -1) b11 " 1# +1) 1* 1, -0+ b1 " 0$ +0+ 0- #254000000 0! #255000000 1! -0) b0 " 0# +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 9bab4248..7dcf26c5 100644 --- a/crates/fayalite/tests/sim/expected/shift_register.txt +++ b/crates/fayalite/tests/sim/expected/shift_register.txt @@ -128,21 +128,21 @@ Simulation { dest: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.rst", ty: SyncReset }, }, - // at: module-XXXXXXXXXX.rs:1:1 - 6: Const { - dest: StatePartIndex(6), // (0x0) SlotDebugData { name: "", ty: Bool }, - value: 0x0, - }, - // at: module-XXXXXXXXXX.rs:5:1 - 7: IsNonZeroDestIsSmall { + 6: IsNonZeroDestIsSmall { dest: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.clk", ty: Clock }, }, - 8: AndSmall { + 7: 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 + 8: Const { + dest: StatePartIndex(6), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:5:1 9: BranchIfSmallZero { target: 14, value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, @@ -337,9 +337,9 @@ Simulation { }.q, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [], - state_ready_to_run: false, trace_decls: TraceModule { name: "shift_register", children: [ @@ -440,7 +440,7 @@ Simulation { index: StatePartIndex(0), }, state: 0x1, - last_state: 0x1, + last_state: 0x0, }, SimTrace { id: TraceScalarId(1), @@ -509,9 +509,14 @@ Simulation { }, ), ], - instant: 66 μs, clocks_triggered: [ StatePartIndex(1), ], + event_queue: EventQueue(EventQueueData { + instant: 66 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: {}, + waiting_sensitivity_sets_by_compiled_value: {}, .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/shift_register.vcd b/crates/fayalite/tests/sim/expected/shift_register.vcd index 0b5f4290..26726eb0 100644 --- a/crates/fayalite/tests/sim/expected/shift_register.vcd +++ b/crates/fayalite/tests/sim/expected/shift_register.vcd @@ -52,9 +52,9 @@ $end 0! #11000000 1! +1$ 0& 1( -1$ #12000000 0! 1# @@ -67,10 +67,10 @@ $end 0# #15000000 1! +0$ 0% 1& 0( -0$ #16000000 0! 1# @@ -83,23 +83,23 @@ $end 0! #19000000 1! +1$ 1& 0' 1( -1$ #20000000 0! #21000000 1! +0$ 1' 0( -0$ #22000000 0! #23000000 1! -1( 1$ +1( #24000000 0! 0# @@ -120,8 +120,8 @@ $end 0! #31000000 1! -0( 0$ +0( #32000000 0! #33000000 diff --git a/crates/fayalite/tests/sim/expected/sim_fork_join.txt b/crates/fayalite/tests/sim/expected/sim_fork_join.txt new file mode 100644 index 00000000..680fedb9 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_fork_join.txt @@ -0,0 +1,525 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 6, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_fork_join: sim_fork_join).sim_fork_join::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join: sim_fork_join).sim_fork_join::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join: sim_fork_join).sim_fork_join::clocks[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join: sim_fork_join).sim_fork_join::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join: sim_fork_join).sim_fork_join::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join: sim_fork_join).sim_fork_join::outputs[2]", + 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, + 0, + 0, + 49, + 50, + 50, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.clocks, + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.outputs, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.clocks, + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.clocks[0], + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.clocks[1], + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.clocks[2], + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.outputs, + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.outputs[0], + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.outputs[1], + Instance { + name: ::sim_fork_join, + instantiated: Module { + name: sim_fork_join, + .. + }, + }.outputs[2], + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_fork_join::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_fork_join::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join::clocks, + is_input: true, + ty: Array, + .. + }[0], + ModuleIO { + name: sim_fork_join::clocks, + is_input: true, + ty: Array, + .. + }[1], + ModuleIO { + name: sim_fork_join::clocks, + is_input: true, + ty: Array, + .. + }[2], + ModuleIO { + name: sim_fork_join::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ModuleIO { + name: sim_fork_join::outputs, + is_input: false, + ty: Array, 3>, + .. + }[0], + ModuleIO { + name: sim_fork_join::outputs, + is_input: false, + ty: Array, 3>, + .. + }[1], + ModuleIO { + name: sim_fork_join::outputs, + is_input: false, + ty: Array, 3>, + .. + }[2], + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_fork_join::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_fork_join::clocks, + is_input: true, + ty: Array, + .. + }: ModuleIO { + name: sim_fork_join::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join::outputs, + is_input: false, + ty: Array, 3>, + .. + }: ModuleIO { + name: sim_fork_join::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + }, + ], + trace_decls: TraceModule { + name: "sim_fork_join", + children: [ + TraceModuleIO { + name: "clocks", + child: TraceArray { + name: "clocks", + elements: [ + TraceClock { + location: TraceScalarId(0), + name: "[0]", + flow: Source, + }, + TraceClock { + location: TraceScalarId(1), + name: "[1]", + flow: Source, + }, + TraceClock { + location: TraceScalarId(2), + name: "[2]", + flow: Source, + }, + ], + ty: Array, + flow: Source, + }, + ty: Array, + flow: Source, + }, + TraceModuleIO { + name: "outputs", + child: TraceArray { + name: "outputs", + elements: [ + TraceUInt { + location: TraceScalarId(3), + name: "[0]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(4), + name: "[1]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(5), + name: "[2]", + ty: UInt<8>, + flow: Sink, + }, + ], + ty: Array, 3>, + flow: Sink, + }, + ty: Array, 3>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigClock { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigClock { + index: StatePartIndex(2), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigUInt { + index: StatePartIndex(3), + ty: UInt<8>, + }, + state: 0x31, + last_state: 0x31, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigUInt { + index: StatePartIndex(4), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigUInt { + index: StatePartIndex(5), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 648 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 198, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_fork_join: sim_fork_join).sim_fork_join::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_fork_join: sim_fork_join).sim_fork_join::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 198, + .. + }, + }, + ), + }, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_fork_join.vcd b/crates/fayalite/tests/sim/expected/sim_fork_join.vcd new file mode 100644 index 00000000..a420c2b7 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_fork_join.vcd @@ -0,0 +1,1467 @@ +$timescale 1 ps $end +$scope module sim_fork_join $end +$scope struct clocks $end +$var wire 1 ! \[0] $end +$var wire 1 " \[1] $end +$var wire 1 # \[2] $end +$upscope $end +$scope struct outputs $end +$var wire 8 $ \[0] $end +$var wire 8 % \[1] $end +$var wire 8 & \[2] $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +0" +0# +b0 $ +b0 % +b0 & +$end +#1000000 +1! +b1 $ +#2000000 +0! +#3000000 +1! +#4000000 +0! +#5000000 +1! +#6000000 +0! +#7000000 +1! +#8000000 +0! +#9000000 +1! +#10000000 +0! +#11000000 +1! +#12000000 +0! +#13000000 +1! +#14000000 +0! +#15000000 +1" +b1 % +#16000000 +0" +#17000000 +1! +#18000000 +0! +#19000000 +1! +#20000000 +0! +#21000000 +1! +#22000000 +0! +#23000000 +1# +b1 & +#24000000 +0# +#25000000 +1! +b10 $ +#26000000 +0! +#27000000 +1! +#28000000 +0! +#29000000 +1" +b10 % +#30000000 +0" +#31000000 +1! +#32000000 +0! +#33000000 +1! +#34000000 +0! +#35000000 +1! +#36000000 +0! +#37000000 +1" +#38000000 +0" +#39000000 +1" +#40000000 +0" +#41000000 +1! +#42000000 +0! +#43000000 +1! +#44000000 +0! +#45000000 +1" +#46000000 +0" +#47000000 +1# +b10 & +#48000000 +0# +#49000000 +1! +b11 $ +#50000000 +0! +#51000000 +1! +#52000000 +0! +#53000000 +1# +b11 & +#54000000 +0# +#55000000 +1! +#56000000 +0! +#57000000 +1! +#58000000 +0! +#59000000 +1! +#60000000 +0! +#61000000 +1# +#62000000 +0# +#63000000 +1" +b11 % +#64000000 +0" +#65000000 +1! +b100 $ +#66000000 +0! +#67000000 +1! +#68000000 +0! +#69000000 +1# +b100 & +#70000000 +0# +#71000000 +1# +#72000000 +0# +#73000000 +1! +#74000000 +0! +#75000000 +1" +b100 % +#76000000 +0" +#77000000 +1! +b101 $ +#78000000 +0! +#79000000 +1! +#80000000 +0! +#81000000 +1! +#82000000 +0! +#83000000 +1" +b101 % +#84000000 +0" +#85000000 +1! +#86000000 +0! +#87000000 +1" +#88000000 +0" +#89000000 +1! +#90000000 +0! +#91000000 +1" +#92000000 +0" +#93000000 +1! +#94000000 +0! +#95000000 +1# +b101 & +#96000000 +0# +#97000000 +1! +b110 $ +#98000000 +0! +#99000000 +1" +b110 % +#100000000 +0" +#101000000 +1" +#102000000 +0" +#103000000 +1! +#104000000 +0! +#105000000 +1! +#106000000 +0! +#107000000 +1" +#108000000 +0" +#109000000 +1" +#110000000 +0" +#111000000 +1" +#112000000 +0" +#113000000 +1! +#114000000 +0! +#115000000 +1" +#116000000 +0" +#117000000 +1" +#118000000 +0" +#119000000 +1# +b110 & +#120000000 +0# +#121000000 +1! +b111 $ +#122000000 +0! +#123000000 +1" +b111 % +#124000000 +0" +#125000000 +1# +b111 & +#126000000 +0# +#127000000 +1! +b1000 $ +#128000000 +0! +#129000000 +1! +#130000000 +0! +#131000000 +1" +b1000 % +#132000000 +0" +#133000000 +1# +b1000 & +#134000000 +0# +#135000000 +1" +b1001 % +#136000000 +0" +#137000000 +1! +b1001 $ +#138000000 +0! +#139000000 +1" +#140000000 +0" +#141000000 +1# +b1001 & +#142000000 +0# +#143000000 +1# +b1010 & +#144000000 +0# +#145000000 +1! +b1010 $ +#146000000 +0! +#147000000 +1# +#148000000 +0# +#149000000 +1! +#150000000 +0! +#151000000 +1! +#152000000 +0! +#153000000 +1! +#154000000 +0! +#155000000 +1# +#156000000 +0# +#157000000 +1! +#158000000 +0! +#159000000 +1" +b1010 % +#160000000 +0" +#161000000 +1! +b1011 $ +#162000000 +0! +#163000000 +1# +b1011 & +#164000000 +0# +#165000000 +1! +#166000000 +0! +#167000000 +1# +#168000000 +0# +#169000000 +1! +#170000000 +0! +#171000000 +1# +#172000000 +0# +#173000000 +1" +b1011 % +#174000000 +0" +#175000000 +1! +b1100 $ +#176000000 +0! +#177000000 +1! +#178000000 +0! +#179000000 +1# +b1100 & +#180000000 +0# +#181000000 +1" +b1100 % +#182000000 +0" +#183000000 +1" +b1101 % +#184000000 +0" +#185000000 +1! +b1101 $ +#186000000 +0! +#187000000 +1# +b1101 & +#188000000 +0# +#189000000 +1" +b1110 % +#190000000 +0" +#191000000 +1# +b1110 & +#192000000 +0# +#193000000 +1! +b1110 $ +#194000000 +0! +#195000000 +1# +b1111 & +#196000000 +0# +#197000000 +1# +#198000000 +0# +#199000000 +1! +b1111 $ +#200000000 +0! +#201000000 +1! +#202000000 +0! +#203000000 +1# +#204000000 +0# +#205000000 +1# +#206000000 +0# +#207000000 +1" +b1111 % +#208000000 +0" +#209000000 +1! +b10000 $ +#210000000 +0! +#211000000 +1# +b10000 & +#212000000 +0# +#213000000 +1# +#214000000 +0# +#215000000 +1# +#216000000 +0# +#217000000 +1" +b10000 % +#218000000 +0" +#219000000 +1! +b10001 $ +#220000000 +0! +#221000000 +1! +#222000000 +0! +#223000000 +1! +#224000000 +0! +#225000000 +1" +b10001 % +#226000000 +0" +#227000000 +1! +#228000000 +0! +#229000000 +1! +#230000000 +0! +#231000000 +1" +#232000000 +0" +#233000000 +1" +#234000000 +0" +#235000000 +1! +#236000000 +0! +#237000000 +1! +#238000000 +0! +#239000000 +1# +b10001 & +#240000000 +0# +#241000000 +1" +b10010 % +#242000000 +0" +#243000000 +1! +b10010 $ +#244000000 +0! +#245000000 +1" +#246000000 +0" +#247000000 +1! +#248000000 +0! +#249000000 +1" +#250000000 +0" +#251000000 +1! +#252000000 +0! +#253000000 +1" +#254000000 +0" +#255000000 +1" +#256000000 +0" +#257000000 +1" +#258000000 +0" +#259000000 +1! +#260000000 +0! +#261000000 +1" +#262000000 +0" +#263000000 +1# +b10010 & +#264000000 +0# +#265000000 +1" +b10011 % +#266000000 +0" +#267000000 +1! +b10011 $ +#268000000 +0! +#269000000 +1# +b10011 & +#270000000 +0# +#271000000 +1! +b10100 $ +#272000000 +0! +#273000000 +1" +b10100 % +#274000000 +0" +#275000000 +1! +#276000000 +0! +#277000000 +1# +b10100 & +#278000000 +0# +#279000000 +1" +b10101 % +#280000000 +0" +#281000000 +1" +#282000000 +0" +#283000000 +1! +b10101 $ +#284000000 +0! +#285000000 +1# +b10101 & +#286000000 +0# +#287000000 +1# +b10110 & +#288000000 +0# +#289000000 +1" +b10110 % +#290000000 +0" +#291000000 +1" +#292000000 +0" +#293000000 +1! +b10110 $ +#294000000 +0! +#295000000 +1! +b10111 $ +#296000000 +0! +#297000000 +1" +b10111 % +#298000000 +0" +#299000000 +1" +#300000000 +0" +#301000000 +1! +#302000000 +0! +#303000000 +1" +#304000000 +0" +#305000000 +1" +#306000000 +0" +#307000000 +1" +#308000000 +0" +#309000000 +1! +#310000000 +0! +#311000000 +1# +b10111 & +#312000000 +0# +#313000000 +1" +b11000 % +#314000000 +0" +#315000000 +1" +#316000000 +0" +#317000000 +1" +#318000000 +0" +#319000000 +1! +b11000 $ +#320000000 +0! +#321000000 +1" +#322000000 +0" +#323000000 +1" +#324000000 +0" +#325000000 +1" +#326000000 +0" +#327000000 +1" +#328000000 +0" +#329000000 +1" +#330000000 +0" +#331000000 +1" +#332000000 +0" +#333000000 +1" +#334000000 +0" +#335000000 +1# +b11000 & +#336000000 +0# +#337000000 +1" +b11001 % +#338000000 +0" +#339000000 +1" +#340000000 +0" +#341000000 +1# +b11001 & +#342000000 +0# +#343000000 +1! +b11001 $ +#344000000 +0! +#345000000 +1" +b11010 % +#346000000 +0" +#347000000 +1" +#348000000 +0" +#349000000 +1# +b11010 & +#350000000 +0# +#351000000 +1" +#352000000 +0" +#353000000 +1" +#354000000 +0" +#355000000 +1" +#356000000 +0" +#357000000 +1# +#358000000 +0# +#359000000 +1# +#360000000 +0# +#361000000 +1" +#362000000 +0" +#363000000 +1# +#364000000 +0# +#365000000 +1! +b11010 $ +#366000000 +0! +#367000000 +1! +b11011 $ +#368000000 +0! +#369000000 +1" +b11011 % +#370000000 +0" +#371000000 +1# +b11011 & +#372000000 +0# +#373000000 +1! +b11100 $ +#374000000 +0! +#375000000 +1" +b11100 % +#376000000 +0" +#377000000 +1" +#378000000 +0" +#379000000 +1# +b11100 & +#380000000 +0# +#381000000 +1! +b11101 $ +#382000000 +0! +#383000000 +1# +b11101 & +#384000000 +0# +#385000000 +1" +b11101 % +#386000000 +0" +#387000000 +1# +b11110 & +#388000000 +0# +#389000000 +1" +b11110 % +#390000000 +0" +#391000000 +1! +b11110 $ +#392000000 +0! +#393000000 +1" +b11111 % +#394000000 +0" +#395000000 +1# +b11111 & +#396000000 +0# +#397000000 +1" +#398000000 +0" +#399000000 +1" +#400000000 +0" +#401000000 +1" +#402000000 +0" +#403000000 +1# +#404000000 +0# +#405000000 +1" +#406000000 +0" +#407000000 +1# +#408000000 +0# +#409000000 +1" +#410000000 +0" +#411000000 +1# +#412000000 +0# +#413000000 +1# +#414000000 +0# +#415000000 +1! +b11111 $ +#416000000 +0! +#417000000 +1" +b100000 % +#418000000 +0" +#419000000 +1# +b100000 & +#420000000 +0# +#421000000 +1# +#422000000 +0# +#423000000 +1" +#424000000 +0" +#425000000 +1" +#426000000 +0" +#427000000 +1# +#428000000 +0# +#429000000 +1# +#430000000 +0# +#431000000 +1# +#432000000 +0# +#433000000 +1# +#434000000 +0# +#435000000 +1! +b100000 $ +#436000000 +0! +#437000000 +1! +b100001 $ +#438000000 +0! +#439000000 +1! +#440000000 +0! +#441000000 +1# +b100001 & +#442000000 +0# +#443000000 +1! +#444000000 +0! +#445000000 +1! +#446000000 +0! +#447000000 +1" +b100001 % +#448000000 +0" +#449000000 +1# +b100010 & +#450000000 +0# +#451000000 +1! +b100010 $ +#452000000 +0! +#453000000 +1! +#454000000 +0! +#455000000 +1# +#456000000 +0# +#457000000 +1# +#458000000 +0# +#459000000 +1! +#460000000 +0! +#461000000 +1" +b100010 % +#462000000 +0" +#463000000 +1! +b100011 $ +#464000000 +0! +#465000000 +1# +b100011 & +#466000000 +0# +#467000000 +1! +#468000000 +0! +#469000000 +1" +b100011 % +#470000000 +0" +#471000000 +1" +b100100 % +#472000000 +0" +#473000000 +1# +b100100 & +#474000000 +0# +#475000000 +1! +b100100 $ +#476000000 +0! +#477000000 +1" +b100101 % +#478000000 +0" +#479000000 +1# +b100101 & +#480000000 +0# +#481000000 +1# +#482000000 +0# +#483000000 +1! +b100101 $ +#484000000 +0! +#485000000 +1# +b100110 & +#486000000 +0# +#487000000 +1! +b100110 $ +#488000000 +0! +#489000000 +1# +#490000000 +0# +#491000000 +1! +#492000000 +0! +#493000000 +1# +#494000000 +0# +#495000000 +1" +b100110 % +#496000000 +0" +#497000000 +1# +b100111 & +#498000000 +0# +#499000000 +1! +b100111 $ +#500000000 +0! +#501000000 +1# +#502000000 +0# +#503000000 +1# +#504000000 +0# +#505000000 +1# +#506000000 +0# +#507000000 +1" +b100111 % +#508000000 +0" +#509000000 +1! +b101000 $ +#510000000 +0! +#511000000 +1! +#512000000 +0! +#513000000 +1# +b101000 & +#514000000 +0# +#515000000 +1" +b101000 % +#516000000 +0" +#517000000 +1! +b101001 $ +#518000000 +0! +#519000000 +1" +b101001 % +#520000000 +0" +#521000000 +1# +b101001 & +#522000000 +0# +#523000000 +1" +b101010 % +#524000000 +0" +#525000000 +1! +b101010 $ +#526000000 +0! +#527000000 +1# +b101010 & +#528000000 +0# +#529000000 +1# +b101011 & +#530000000 +0# +#531000000 +1" +b101011 % +#532000000 +0" +#533000000 +1" +#534000000 +0" +#535000000 +1! +b101011 $ +#536000000 +0! +#537000000 +1# +b101100 & +#538000000 +0# +#539000000 +1" +b101100 % +#540000000 +0" +#541000000 +1" +#542000000 +0" +#543000000 +1" +#544000000 +0" +#545000000 +1# +#546000000 +0# +#547000000 +1" +#548000000 +0" +#549000000 +1" +#550000000 +0" +#551000000 +1# +#552000000 +0# +#553000000 +1# +#554000000 +0# +#555000000 +1" +#556000000 +0" +#557000000 +1# +#558000000 +0# +#559000000 +1! +b101100 $ +#560000000 +0! +#561000000 +1# +b101101 & +#562000000 +0# +#563000000 +1" +b101101 % +#564000000 +0" +#565000000 +1# +#566000000 +0# +#567000000 +1" +#568000000 +0" +#569000000 +1# +#570000000 +0# +#571000000 +1" +#572000000 +0" +#573000000 +1# +#574000000 +0# +#575000000 +1# +#576000000 +0# +#577000000 +1# +#578000000 +0# +#579000000 +1# +#580000000 +0# +#581000000 +1! +b101101 $ +#582000000 +0! +#583000000 +1! +b101110 $ +#584000000 +0! +#585000000 +1# +b101110 & +#586000000 +0# +#587000000 +1# +#588000000 +0# +#589000000 +1! +#590000000 +0! +#591000000 +1" +b101110 % +#592000000 +0" +#593000000 +1# +b101111 & +#594000000 +0# +#595000000 +1# +#596000000 +0# +#597000000 +1! +b101111 $ +#598000000 +0! +#599000000 +1# +#600000000 +0# +#601000000 +1# +#602000000 +0# +#603000000 +1# +#604000000 +0# +#605000000 +1" +b101111 % +#606000000 +0" +#607000000 +1! +b110000 $ +#608000000 +0! +#609000000 +1# +b110000 & +#610000000 +0# +#611000000 +1# +#612000000 +0# +#613000000 +1" +b110000 % +#614000000 +0" +#615000000 +1" +b110001 % +#616000000 +0" +#617000000 +1# +b110001 & +#618000000 +0# +#619000000 +1# +#620000000 +0# +#621000000 +1" +#622000000 +0" +#623000000 +1# +#624000000 +0# +#625000000 +1# +#626000000 +0# +#627000000 +1# +#628000000 +0# +#629000000 +1# +#630000000 +0# +#631000000 +1! +b110001 $ +#632000000 +0! +#633000000 +1# +b110010 & +#634000000 +0# +#635000000 +1# +#636000000 +0# +#637000000 +1# +#638000000 +0# +#639000000 +1" +b110010 % +#640000000 +0" +#641000000 +1# +#642000000 +0# +#643000000 +1# +#644000000 +0# +#645000000 +1# +#646000000 +0# +#647000000 +1# +#648000000 +0# diff --git a/crates/fayalite/tests/sim/expected/sim_fork_join_scope.txt b/crates/fayalite/tests/sim/expected/sim_fork_join_scope.txt new file mode 100644 index 00000000..40d16a90 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_fork_join_scope.txt @@ -0,0 +1,525 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 6, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::clocks[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::outputs[2]", + 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, + 0, + 0, + 49, + 50, + 50, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks, + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks, + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks[0], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks[1], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks[2], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs, + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs[0], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs[1], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs[2], + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }[0], + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }[1], + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }[2], + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }[0], + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }[1], + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }[2], + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }: ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }: ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + }, + ], + trace_decls: TraceModule { + name: "sim_fork_join_scope", + children: [ + TraceModuleIO { + name: "clocks", + child: TraceArray { + name: "clocks", + elements: [ + TraceClock { + location: TraceScalarId(0), + name: "[0]", + flow: Source, + }, + TraceClock { + location: TraceScalarId(1), + name: "[1]", + flow: Source, + }, + TraceClock { + location: TraceScalarId(2), + name: "[2]", + flow: Source, + }, + ], + ty: Array, + flow: Source, + }, + ty: Array, + flow: Source, + }, + TraceModuleIO { + name: "outputs", + child: TraceArray { + name: "outputs", + elements: [ + TraceUInt { + location: TraceScalarId(3), + name: "[0]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(4), + name: "[1]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(5), + name: "[2]", + ty: UInt<8>, + flow: Sink, + }, + ], + ty: Array, 3>, + flow: Sink, + }, + ty: Array, 3>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigClock { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigClock { + index: StatePartIndex(2), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigUInt { + index: StatePartIndex(3), + ty: UInt<8>, + }, + state: 0x31, + last_state: 0x31, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigUInt { + index: StatePartIndex(4), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigUInt { + index: StatePartIndex(5), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 648 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 198, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 198, + .. + }, + }, + ), + }, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_fork_join_scope.vcd b/crates/fayalite/tests/sim/expected/sim_fork_join_scope.vcd new file mode 100644 index 00000000..555e83ed --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_fork_join_scope.vcd @@ -0,0 +1,1467 @@ +$timescale 1 ps $end +$scope module sim_fork_join_scope $end +$scope struct clocks $end +$var wire 1 ! \[0] $end +$var wire 1 " \[1] $end +$var wire 1 # \[2] $end +$upscope $end +$scope struct outputs $end +$var wire 8 $ \[0] $end +$var wire 8 % \[1] $end +$var wire 8 & \[2] $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +0" +0# +b0 $ +b0 % +b0 & +$end +#1000000 +1! +b1 $ +#2000000 +0! +#3000000 +1! +#4000000 +0! +#5000000 +1! +#6000000 +0! +#7000000 +1! +#8000000 +0! +#9000000 +1! +#10000000 +0! +#11000000 +1! +#12000000 +0! +#13000000 +1! +#14000000 +0! +#15000000 +1" +b1 % +#16000000 +0" +#17000000 +1! +#18000000 +0! +#19000000 +1! +#20000000 +0! +#21000000 +1! +#22000000 +0! +#23000000 +1# +b1 & +#24000000 +0# +#25000000 +1! +b10 $ +#26000000 +0! +#27000000 +1! +#28000000 +0! +#29000000 +1" +b10 % +#30000000 +0" +#31000000 +1! +#32000000 +0! +#33000000 +1! +#34000000 +0! +#35000000 +1! +#36000000 +0! +#37000000 +1" +#38000000 +0" +#39000000 +1" +#40000000 +0" +#41000000 +1! +#42000000 +0! +#43000000 +1! +#44000000 +0! +#45000000 +1" +#46000000 +0" +#47000000 +1# +b10 & +#48000000 +0# +#49000000 +1! +b11 $ +#50000000 +0! +#51000000 +1! +#52000000 +0! +#53000000 +1# +b11 & +#54000000 +0# +#55000000 +1! +#56000000 +0! +#57000000 +1! +#58000000 +0! +#59000000 +1! +#60000000 +0! +#61000000 +1# +#62000000 +0# +#63000000 +1" +b11 % +#64000000 +0" +#65000000 +1! +b100 $ +#66000000 +0! +#67000000 +1! +#68000000 +0! +#69000000 +1# +b100 & +#70000000 +0# +#71000000 +1# +#72000000 +0# +#73000000 +1! +#74000000 +0! +#75000000 +1" +b100 % +#76000000 +0" +#77000000 +1! +b101 $ +#78000000 +0! +#79000000 +1! +#80000000 +0! +#81000000 +1! +#82000000 +0! +#83000000 +1" +b101 % +#84000000 +0" +#85000000 +1! +#86000000 +0! +#87000000 +1" +#88000000 +0" +#89000000 +1! +#90000000 +0! +#91000000 +1" +#92000000 +0" +#93000000 +1! +#94000000 +0! +#95000000 +1# +b101 & +#96000000 +0# +#97000000 +1! +b110 $ +#98000000 +0! +#99000000 +1" +b110 % +#100000000 +0" +#101000000 +1" +#102000000 +0" +#103000000 +1! +#104000000 +0! +#105000000 +1! +#106000000 +0! +#107000000 +1" +#108000000 +0" +#109000000 +1" +#110000000 +0" +#111000000 +1" +#112000000 +0" +#113000000 +1! +#114000000 +0! +#115000000 +1" +#116000000 +0" +#117000000 +1" +#118000000 +0" +#119000000 +1# +b110 & +#120000000 +0# +#121000000 +1! +b111 $ +#122000000 +0! +#123000000 +1" +b111 % +#124000000 +0" +#125000000 +1# +b111 & +#126000000 +0# +#127000000 +1! +b1000 $ +#128000000 +0! +#129000000 +1! +#130000000 +0! +#131000000 +1" +b1000 % +#132000000 +0" +#133000000 +1# +b1000 & +#134000000 +0# +#135000000 +1" +b1001 % +#136000000 +0" +#137000000 +1! +b1001 $ +#138000000 +0! +#139000000 +1" +#140000000 +0" +#141000000 +1# +b1001 & +#142000000 +0# +#143000000 +1# +b1010 & +#144000000 +0# +#145000000 +1! +b1010 $ +#146000000 +0! +#147000000 +1# +#148000000 +0# +#149000000 +1! +#150000000 +0! +#151000000 +1! +#152000000 +0! +#153000000 +1! +#154000000 +0! +#155000000 +1# +#156000000 +0# +#157000000 +1! +#158000000 +0! +#159000000 +1" +b1010 % +#160000000 +0" +#161000000 +1! +b1011 $ +#162000000 +0! +#163000000 +1# +b1011 & +#164000000 +0# +#165000000 +1! +#166000000 +0! +#167000000 +1# +#168000000 +0# +#169000000 +1! +#170000000 +0! +#171000000 +1# +#172000000 +0# +#173000000 +1" +b1011 % +#174000000 +0" +#175000000 +1! +b1100 $ +#176000000 +0! +#177000000 +1! +#178000000 +0! +#179000000 +1# +b1100 & +#180000000 +0# +#181000000 +1" +b1100 % +#182000000 +0" +#183000000 +1" +b1101 % +#184000000 +0" +#185000000 +1! +b1101 $ +#186000000 +0! +#187000000 +1# +b1101 & +#188000000 +0# +#189000000 +1" +b1110 % +#190000000 +0" +#191000000 +1# +b1110 & +#192000000 +0# +#193000000 +1! +b1110 $ +#194000000 +0! +#195000000 +1# +b1111 & +#196000000 +0# +#197000000 +1# +#198000000 +0# +#199000000 +1! +b1111 $ +#200000000 +0! +#201000000 +1! +#202000000 +0! +#203000000 +1# +#204000000 +0# +#205000000 +1# +#206000000 +0# +#207000000 +1" +b1111 % +#208000000 +0" +#209000000 +1! +b10000 $ +#210000000 +0! +#211000000 +1# +b10000 & +#212000000 +0# +#213000000 +1# +#214000000 +0# +#215000000 +1# +#216000000 +0# +#217000000 +1" +b10000 % +#218000000 +0" +#219000000 +1! +b10001 $ +#220000000 +0! +#221000000 +1! +#222000000 +0! +#223000000 +1! +#224000000 +0! +#225000000 +1" +b10001 % +#226000000 +0" +#227000000 +1! +#228000000 +0! +#229000000 +1! +#230000000 +0! +#231000000 +1" +#232000000 +0" +#233000000 +1" +#234000000 +0" +#235000000 +1! +#236000000 +0! +#237000000 +1! +#238000000 +0! +#239000000 +1# +b10001 & +#240000000 +0# +#241000000 +1" +b10010 % +#242000000 +0" +#243000000 +1! +b10010 $ +#244000000 +0! +#245000000 +1" +#246000000 +0" +#247000000 +1! +#248000000 +0! +#249000000 +1" +#250000000 +0" +#251000000 +1! +#252000000 +0! +#253000000 +1" +#254000000 +0" +#255000000 +1" +#256000000 +0" +#257000000 +1" +#258000000 +0" +#259000000 +1! +#260000000 +0! +#261000000 +1" +#262000000 +0" +#263000000 +1# +b10010 & +#264000000 +0# +#265000000 +1" +b10011 % +#266000000 +0" +#267000000 +1! +b10011 $ +#268000000 +0! +#269000000 +1# +b10011 & +#270000000 +0# +#271000000 +1! +b10100 $ +#272000000 +0! +#273000000 +1" +b10100 % +#274000000 +0" +#275000000 +1! +#276000000 +0! +#277000000 +1# +b10100 & +#278000000 +0# +#279000000 +1" +b10101 % +#280000000 +0" +#281000000 +1" +#282000000 +0" +#283000000 +1! +b10101 $ +#284000000 +0! +#285000000 +1# +b10101 & +#286000000 +0# +#287000000 +1# +b10110 & +#288000000 +0# +#289000000 +1" +b10110 % +#290000000 +0" +#291000000 +1" +#292000000 +0" +#293000000 +1! +b10110 $ +#294000000 +0! +#295000000 +1! +b10111 $ +#296000000 +0! +#297000000 +1" +b10111 % +#298000000 +0" +#299000000 +1" +#300000000 +0" +#301000000 +1! +#302000000 +0! +#303000000 +1" +#304000000 +0" +#305000000 +1" +#306000000 +0" +#307000000 +1" +#308000000 +0" +#309000000 +1! +#310000000 +0! +#311000000 +1# +b10111 & +#312000000 +0# +#313000000 +1" +b11000 % +#314000000 +0" +#315000000 +1" +#316000000 +0" +#317000000 +1" +#318000000 +0" +#319000000 +1! +b11000 $ +#320000000 +0! +#321000000 +1" +#322000000 +0" +#323000000 +1" +#324000000 +0" +#325000000 +1" +#326000000 +0" +#327000000 +1" +#328000000 +0" +#329000000 +1" +#330000000 +0" +#331000000 +1" +#332000000 +0" +#333000000 +1" +#334000000 +0" +#335000000 +1# +b11000 & +#336000000 +0# +#337000000 +1" +b11001 % +#338000000 +0" +#339000000 +1" +#340000000 +0" +#341000000 +1# +b11001 & +#342000000 +0# +#343000000 +1! +b11001 $ +#344000000 +0! +#345000000 +1" +b11010 % +#346000000 +0" +#347000000 +1" +#348000000 +0" +#349000000 +1# +b11010 & +#350000000 +0# +#351000000 +1" +#352000000 +0" +#353000000 +1" +#354000000 +0" +#355000000 +1" +#356000000 +0" +#357000000 +1# +#358000000 +0# +#359000000 +1# +#360000000 +0# +#361000000 +1" +#362000000 +0" +#363000000 +1# +#364000000 +0# +#365000000 +1! +b11010 $ +#366000000 +0! +#367000000 +1! +b11011 $ +#368000000 +0! +#369000000 +1" +b11011 % +#370000000 +0" +#371000000 +1# +b11011 & +#372000000 +0# +#373000000 +1! +b11100 $ +#374000000 +0! +#375000000 +1" +b11100 % +#376000000 +0" +#377000000 +1" +#378000000 +0" +#379000000 +1# +b11100 & +#380000000 +0# +#381000000 +1! +b11101 $ +#382000000 +0! +#383000000 +1# +b11101 & +#384000000 +0# +#385000000 +1" +b11101 % +#386000000 +0" +#387000000 +1# +b11110 & +#388000000 +0# +#389000000 +1" +b11110 % +#390000000 +0" +#391000000 +1! +b11110 $ +#392000000 +0! +#393000000 +1" +b11111 % +#394000000 +0" +#395000000 +1# +b11111 & +#396000000 +0# +#397000000 +1" +#398000000 +0" +#399000000 +1" +#400000000 +0" +#401000000 +1" +#402000000 +0" +#403000000 +1# +#404000000 +0# +#405000000 +1" +#406000000 +0" +#407000000 +1# +#408000000 +0# +#409000000 +1" +#410000000 +0" +#411000000 +1# +#412000000 +0# +#413000000 +1# +#414000000 +0# +#415000000 +1! +b11111 $ +#416000000 +0! +#417000000 +1" +b100000 % +#418000000 +0" +#419000000 +1# +b100000 & +#420000000 +0# +#421000000 +1# +#422000000 +0# +#423000000 +1" +#424000000 +0" +#425000000 +1" +#426000000 +0" +#427000000 +1# +#428000000 +0# +#429000000 +1# +#430000000 +0# +#431000000 +1# +#432000000 +0# +#433000000 +1# +#434000000 +0# +#435000000 +1! +b100000 $ +#436000000 +0! +#437000000 +1! +b100001 $ +#438000000 +0! +#439000000 +1! +#440000000 +0! +#441000000 +1# +b100001 & +#442000000 +0# +#443000000 +1! +#444000000 +0! +#445000000 +1! +#446000000 +0! +#447000000 +1" +b100001 % +#448000000 +0" +#449000000 +1# +b100010 & +#450000000 +0# +#451000000 +1! +b100010 $ +#452000000 +0! +#453000000 +1! +#454000000 +0! +#455000000 +1# +#456000000 +0# +#457000000 +1# +#458000000 +0# +#459000000 +1! +#460000000 +0! +#461000000 +1" +b100010 % +#462000000 +0" +#463000000 +1! +b100011 $ +#464000000 +0! +#465000000 +1# +b100011 & +#466000000 +0# +#467000000 +1! +#468000000 +0! +#469000000 +1" +b100011 % +#470000000 +0" +#471000000 +1" +b100100 % +#472000000 +0" +#473000000 +1# +b100100 & +#474000000 +0# +#475000000 +1! +b100100 $ +#476000000 +0! +#477000000 +1" +b100101 % +#478000000 +0" +#479000000 +1# +b100101 & +#480000000 +0# +#481000000 +1# +#482000000 +0# +#483000000 +1! +b100101 $ +#484000000 +0! +#485000000 +1# +b100110 & +#486000000 +0# +#487000000 +1! +b100110 $ +#488000000 +0! +#489000000 +1# +#490000000 +0# +#491000000 +1! +#492000000 +0! +#493000000 +1# +#494000000 +0# +#495000000 +1" +b100110 % +#496000000 +0" +#497000000 +1# +b100111 & +#498000000 +0# +#499000000 +1! +b100111 $ +#500000000 +0! +#501000000 +1# +#502000000 +0# +#503000000 +1# +#504000000 +0# +#505000000 +1# +#506000000 +0# +#507000000 +1" +b100111 % +#508000000 +0" +#509000000 +1! +b101000 $ +#510000000 +0! +#511000000 +1! +#512000000 +0! +#513000000 +1# +b101000 & +#514000000 +0# +#515000000 +1" +b101000 % +#516000000 +0" +#517000000 +1! +b101001 $ +#518000000 +0! +#519000000 +1" +b101001 % +#520000000 +0" +#521000000 +1# +b101001 & +#522000000 +0# +#523000000 +1" +b101010 % +#524000000 +0" +#525000000 +1! +b101010 $ +#526000000 +0! +#527000000 +1# +b101010 & +#528000000 +0# +#529000000 +1# +b101011 & +#530000000 +0# +#531000000 +1" +b101011 % +#532000000 +0" +#533000000 +1" +#534000000 +0" +#535000000 +1! +b101011 $ +#536000000 +0! +#537000000 +1# +b101100 & +#538000000 +0# +#539000000 +1" +b101100 % +#540000000 +0" +#541000000 +1" +#542000000 +0" +#543000000 +1" +#544000000 +0" +#545000000 +1# +#546000000 +0# +#547000000 +1" +#548000000 +0" +#549000000 +1" +#550000000 +0" +#551000000 +1# +#552000000 +0# +#553000000 +1# +#554000000 +0# +#555000000 +1" +#556000000 +0" +#557000000 +1# +#558000000 +0# +#559000000 +1! +b101100 $ +#560000000 +0! +#561000000 +1# +b101101 & +#562000000 +0# +#563000000 +1" +b101101 % +#564000000 +0" +#565000000 +1# +#566000000 +0# +#567000000 +1" +#568000000 +0" +#569000000 +1# +#570000000 +0# +#571000000 +1" +#572000000 +0" +#573000000 +1# +#574000000 +0# +#575000000 +1# +#576000000 +0# +#577000000 +1# +#578000000 +0# +#579000000 +1# +#580000000 +0# +#581000000 +1! +b101101 $ +#582000000 +0! +#583000000 +1! +b101110 $ +#584000000 +0! +#585000000 +1# +b101110 & +#586000000 +0# +#587000000 +1# +#588000000 +0# +#589000000 +1! +#590000000 +0! +#591000000 +1" +b101110 % +#592000000 +0" +#593000000 +1# +b101111 & +#594000000 +0# +#595000000 +1# +#596000000 +0# +#597000000 +1! +b101111 $ +#598000000 +0! +#599000000 +1# +#600000000 +0# +#601000000 +1# +#602000000 +0# +#603000000 +1# +#604000000 +0# +#605000000 +1" +b101111 % +#606000000 +0" +#607000000 +1! +b110000 $ +#608000000 +0! +#609000000 +1# +b110000 & +#610000000 +0# +#611000000 +1# +#612000000 +0# +#613000000 +1" +b110000 % +#614000000 +0" +#615000000 +1" +b110001 % +#616000000 +0" +#617000000 +1# +b110001 & +#618000000 +0# +#619000000 +1# +#620000000 +0# +#621000000 +1" +#622000000 +0" +#623000000 +1# +#624000000 +0# +#625000000 +1# +#626000000 +0# +#627000000 +1# +#628000000 +0# +#629000000 +1# +#630000000 +0# +#631000000 +1! +b110001 $ +#632000000 +0! +#633000000 +1# +b110010 & +#634000000 +0# +#635000000 +1# +#636000000 +0# +#637000000 +1# +#638000000 +0# +#639000000 +1" +b110010 % +#640000000 +0" +#641000000 +1# +#642000000 +0# +#643000000 +1# +#644000000 +0# +#645000000 +1# +#646000000 +0# +#647000000 +1# +#648000000 +0# diff --git a/crates/fayalite/tests/sim/expected/sim_only_connects.txt b/crates/fayalite/tests/sim/expected/sim_only_connects.txt index 114b3138..15456d20 100644 --- a/crates/fayalite/tests/sim/expected/sim_only_connects.txt +++ b/crates/fayalite/tests/sim/expected/sim_only_connects.txt @@ -557,6 +557,7 @@ Simulation { }.out3, }, did_initial_settle: true, + clocks_for_past: {}, }, extern_modules: [ SimulationExternModuleState { @@ -635,6 +636,7 @@ Simulation { }, }, did_initial_settle: true, + clocks_for_past: {}, }, sim: ExternModuleSimulation { generator: SimGeneratorFn { @@ -717,52 +719,6 @@ Simulation { 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 { @@ -840,6 +796,7 @@ Simulation { }, }, did_initial_settle: true, + clocks_for_past: {}, }, sim: ExternModuleSimulation { generator: SimGeneratorFn { @@ -922,55 +879,8 @@ Simulation { 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: [ @@ -1628,9 +1538,214 @@ Simulation { }, ), ], - instant: 16 μs, clocks_triggered: [ StatePartIndex(1), ], + event_queue: EventQueue(EventQueueData { + instant: 16 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 30, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::cd.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: 4, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 6, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + SensitivitySet { + id: 31, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::cd.clk", + 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, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::cd.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: 4, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 6, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 30, + .. + }, + }, + ), + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::cd.clk", + 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, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 31, + .. + }, + }, + ), + }, .. } \ 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 index 2f464c0e..1e4c2497 100644 --- a/crates/fayalite/tests/sim/expected/sim_only_connects.vcd +++ b/crates/fayalite/tests/sim/expected/sim_only_connects.vcd @@ -72,22 +72,22 @@ s{} 8 $end #1000000 1! +s{\"extra\":\x20\"value\"} $ 1' +s{\"extra\":\x20\"value\"} ) 1+ +s{\"extra\":\x20\"value\"} - 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\"} . 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 +s{\"bar\":\x20\"baz\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} & +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\"} 8 #2000000 0! 0" @@ -107,9 +107,6 @@ 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' diff --git a/crates/fayalite/tests/sim/expected/sim_read_past.txt b/crates/fayalite/tests/sim/expected/sim_read_past.txt new file mode 100644 index 00000000..475943ec --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_read_past.txt @@ -0,0 +1,9724 @@ +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: 48, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[2]", + 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:2:1 + 0: IsNonZeroDestIsSmall { + dest: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", ty: Clock }, + }, + 1: 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 }, + }, + 2: IsNonZeroDestIsSmall { + dest: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", ty: Clock }, + }, + 3: 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 }, + }, + 4: IsNonZeroDestIsSmall { + dest: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(0), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", ty: Clock }, + }, + 5: AndSmall { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(0), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + 6: BranchIfSmallZero { + target: 10, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 7: Copy { + dest: StatePartIndex(12), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[0]", ty: Clock }, + src: StatePartIndex(0), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", ty: Clock }, + }, + 8: Copy { + dest: StatePartIndex(13), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[1]", ty: Clock }, + src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", ty: Clock }, + }, + 9: Copy { + dest: StatePartIndex(14), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[2]", ty: Clock }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:3:1 + 10: BranchIfSmallZero { + target: 14, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 11: Copy { + dest: StatePartIndex(15), // (0x30) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[0]", ty: UInt<8> }, + src: StatePartIndex(3), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", ty: UInt<8> }, + }, + 12: Copy { + dest: StatePartIndex(16), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[1]", ty: UInt<8> }, + src: StatePartIndex(4), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", ty: UInt<8> }, + }, + 13: Copy { + dest: StatePartIndex(17), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[2]", ty: UInt<8> }, + src: StatePartIndex(5), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 14: BranchIfSmallZero { + target: 18, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 15: Copy { + dest: StatePartIndex(18), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[0]", ty: Clock }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", ty: Clock }, + }, + 16: Copy { + dest: StatePartIndex(19), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[1]", ty: Clock }, + src: StatePartIndex(7), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", ty: Clock }, + }, + 17: Copy { + dest: StatePartIndex(20), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[2]", ty: Clock }, + src: StatePartIndex(8), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 18: BranchIfSmallZero { + target: 22, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 19: Copy { + dest: StatePartIndex(21), // (0x30) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[0]", ty: UInt<8> }, + src: StatePartIndex(9), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", ty: UInt<8> }, + }, + 20: Copy { + dest: StatePartIndex(22), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[1]", ty: UInt<8> }, + src: StatePartIndex(10), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", ty: UInt<8> }, + }, + 21: Copy { + dest: StatePartIndex(23), // (0x30) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[2]", ty: UInt<8> }, + src: StatePartIndex(11), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:2:1 + 22: BranchIfSmallZero { + target: 26, + value: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 23: Copy { + dest: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[0]", ty: Clock }, + src: StatePartIndex(0), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", ty: Clock }, + }, + 24: Copy { + dest: StatePartIndex(25), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[1]", ty: Clock }, + src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", ty: Clock }, + }, + 25: Copy { + dest: StatePartIndex(26), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[2]", ty: Clock }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:3:1 + 26: BranchIfSmallZero { + target: 30, + value: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 27: Copy { + dest: StatePartIndex(27), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[0]", ty: UInt<8> }, + src: StatePartIndex(3), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", ty: UInt<8> }, + }, + 28: Copy { + dest: StatePartIndex(28), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[1]", ty: UInt<8> }, + src: StatePartIndex(4), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", ty: UInt<8> }, + }, + 29: Copy { + dest: StatePartIndex(29), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[2]", ty: UInt<8> }, + src: StatePartIndex(5), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 30: BranchIfSmallZero { + target: 34, + value: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 31: Copy { + dest: StatePartIndex(30), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[0]", ty: Clock }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", ty: Clock }, + }, + 32: Copy { + dest: StatePartIndex(31), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[1]", ty: Clock }, + src: StatePartIndex(7), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", ty: Clock }, + }, + 33: Copy { + dest: StatePartIndex(32), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[2]", ty: Clock }, + src: StatePartIndex(8), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 34: BranchIfSmallZero { + target: 38, + value: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 35: Copy { + dest: StatePartIndex(33), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[0]", ty: UInt<8> }, + src: StatePartIndex(9), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", ty: UInt<8> }, + }, + 36: Copy { + dest: StatePartIndex(34), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[1]", ty: UInt<8> }, + src: StatePartIndex(10), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", ty: UInt<8> }, + }, + 37: Copy { + dest: StatePartIndex(35), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[2]", ty: UInt<8> }, + src: StatePartIndex(11), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:2:1 + 38: BranchIfSmallZero { + target: 42, + value: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 39: Copy { + dest: StatePartIndex(36), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[0]", ty: Clock }, + src: StatePartIndex(0), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", ty: Clock }, + }, + 40: Copy { + dest: StatePartIndex(37), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[1]", ty: Clock }, + src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", ty: Clock }, + }, + 41: Copy { + dest: StatePartIndex(38), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[2]", ty: Clock }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:3:1 + 42: BranchIfSmallZero { + target: 46, + value: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 43: Copy { + dest: StatePartIndex(39), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[0]", ty: UInt<8> }, + src: StatePartIndex(3), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", ty: UInt<8> }, + }, + 44: Copy { + dest: StatePartIndex(40), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[1]", ty: UInt<8> }, + src: StatePartIndex(4), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", ty: UInt<8> }, + }, + 45: Copy { + dest: StatePartIndex(41), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[2]", ty: UInt<8> }, + src: StatePartIndex(5), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 46: BranchIfSmallZero { + target: 50, + value: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 47: Copy { + dest: StatePartIndex(42), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[0]", ty: Clock }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", ty: Clock }, + }, + 48: Copy { + dest: StatePartIndex(43), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[1]", ty: Clock }, + src: StatePartIndex(7), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", ty: Clock }, + }, + 49: Copy { + dest: StatePartIndex(44), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[2]", ty: Clock }, + src: StatePartIndex(8), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 50: BranchIfSmallZero { + target: 54, + value: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 51: Copy { + dest: StatePartIndex(45), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[0]", ty: UInt<8> }, + src: StatePartIndex(9), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", ty: UInt<8> }, + }, + 52: Copy { + dest: StatePartIndex(46), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[1]", ty: UInt<8> }, + src: StatePartIndex(10), // (0x31) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", ty: UInt<8> }, + }, + 53: Copy { + dest: StatePartIndex(47), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[2]", ty: UInt<8> }, + src: StatePartIndex(11), // (0x32) SlotDebugData { name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:2:1 + 54: XorSmallImmediate { + dest: StatePartIndex(0), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 55: XorSmallImmediate { + dest: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 56: 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 + 57: Return, + ], + .. + }, + pc: 57, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [ + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + ], + }, + big_slots: StatePart { + value: [ + 0, + 0, + 0, + 49, + 50, + 50, + 0, + 1, + 0, + 49, + 49, + 50, + 1, + 0, + 0, + 48, + 49, + 49, + 0, + 0, + 1, + 48, + 49, + 48, + 0, + 1, + 0, + 49, + 49, + 50, + 0, + 0, + 1, + 49, + 49, + 49, + 0, + 0, + 1, + 49, + 50, + 50, + 0, + 1, + 0, + 49, + 49, + 50, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.clocks, + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.outputs, + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_clocks, + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_outputs, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.clocks, + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.clocks[0], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.clocks[1], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.clocks[2], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.outputs, + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.outputs[0], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.outputs[1], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.outputs[2], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_clocks, + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_clocks[0], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_clocks[1], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_clocks[2], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_outputs, + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_outputs[0], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_outputs[1], + Instance { + name: ::sim_read_past, + instantiated: Module { + name: sim_read_past, + .. + }, + }.past_outputs[2], + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_read_past::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_read_past::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ModuleIO { + name: sim_read_past::past_clocks, + is_input: false, + ty: Array, + .. + }, + ModuleIO { + name: sim_read_past::past_outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_read_past::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_read_past::clocks, + is_input: true, + ty: Array, + .. + }[0], + ModuleIO { + name: sim_read_past::clocks, + is_input: true, + ty: Array, + .. + }[1], + ModuleIO { + name: sim_read_past::clocks, + is_input: true, + ty: Array, + .. + }[2], + ModuleIO { + name: sim_read_past::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ModuleIO { + name: sim_read_past::outputs, + is_input: false, + ty: Array, 3>, + .. + }[0], + ModuleIO { + name: sim_read_past::outputs, + is_input: false, + ty: Array, 3>, + .. + }[1], + ModuleIO { + name: sim_read_past::outputs, + is_input: false, + ty: Array, 3>, + .. + }[2], + ModuleIO { + name: sim_read_past::past_clocks, + is_input: false, + ty: Array, + .. + }, + ModuleIO { + name: sim_read_past::past_clocks, + is_input: false, + ty: Array, + .. + }[0], + ModuleIO { + name: sim_read_past::past_clocks, + is_input: false, + ty: Array, + .. + }[1], + ModuleIO { + name: sim_read_past::past_clocks, + is_input: false, + ty: Array, + .. + }[2], + ModuleIO { + name: sim_read_past::past_outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ModuleIO { + name: sim_read_past::past_outputs, + is_input: false, + ty: Array, 3>, + .. + }[0], + ModuleIO { + name: sim_read_past::past_outputs, + is_input: false, + ty: Array, 3>, + .. + }[1], + ModuleIO { + name: sim_read_past::past_outputs, + is_input: false, + ty: Array, 3>, + .. + }[2], + }, + did_initial_settle: true, + clocks_for_past: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimulationExternModuleClockForPast { + current_to_past_map: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + 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: 0, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[2]", + 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: 12, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + 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: 6, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[2]", + 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: 18, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 6, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 3, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 15, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 3, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 9, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 21, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 9, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[0]", + 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: 12, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + 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, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[1]", + 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: 13, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 1, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + 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: 2, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[0])[2]", + 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: 14, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 2, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + 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: 6, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[0]", + 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: 18, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 6, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + 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: 7, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[1]", + 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: 19, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 7, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + 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: 8, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[0])[2]", + 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: 20, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 8, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 3, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 15, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 3, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 16, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 4, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 5, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[0])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 17, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 5, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 9, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 21, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 9, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 10, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 22, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 10, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 11, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[0])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 23, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 11, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + }, + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + 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, + }: SimulationExternModuleClockForPast { + current_to_past_map: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + 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: 0, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[2]", + 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: 24, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + 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: 6, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[2]", + 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: 30, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 6, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 3, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 27, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 3, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 9, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 33, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 9, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[0]", + 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: 24, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + 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, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[1]", + 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: 25, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 1, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + 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: 2, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[1])[2]", + 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: 26, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 2, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + 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: 6, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[0]", + 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: 30, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 6, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + 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: 7, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[1]", + 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: 31, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 7, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + 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: 8, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[1])[2]", + 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: 32, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 8, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 3, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 27, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 3, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 28, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 4, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 5, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[1])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 29, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 5, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 9, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 33, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 9, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 10, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 34, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 10, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 11, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[1])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 35, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 11, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + }, + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + 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: 2, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimulationExternModuleClockForPast { + current_to_past_map: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + 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: 0, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[2]", + 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: 36, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + 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: 6, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[2]", + 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: 42, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 6, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 3, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 39, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 3, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 9, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 45, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Array, 3>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Array { + elements_non_empty: [ + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + ], + }, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 9, len: 3 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[0]", + 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: 36, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + 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, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[1]", + 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: 37, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 1, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + 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: 2, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks$past(sim_read_past::clocks[2])[2]", + 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: 38, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 2, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + 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: 6, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[0]", + 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: 42, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[0]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 6, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + 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: 7, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[1]", + 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: 43, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[1]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 7, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + 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: 8, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks$past(sim_read_past::clocks[2])[2]", + 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: 44, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_clocks[2]", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 8, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 3, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 39, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 3, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 40, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 4, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 5, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::outputs$past(sim_read_past::clocks[2])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 41, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 5, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 9, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[0]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 45, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[0]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 9, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 10, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[1]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 46, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[1]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 10, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 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: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 11, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: 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(sim_read_past: sim_read_past).sim_read_past::past_outputs$past(sim_read_past::clocks[2])[2]", + ty: UInt<8>, + }, + ], + .. + }, + 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: 47, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: Some( + ( + CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::past_outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 11, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + ), + ), + }, + }, + }, + }, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_read_past::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_read_past::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ModuleIO { + name: sim_read_past::past_clocks, + is_input: false, + ty: Array, + .. + }, + ModuleIO { + name: sim_read_past::past_outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_read_past::clocks, + is_input: true, + ty: Array, + .. + }: ModuleIO { + name: sim_read_past::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_read_past::outputs, + is_input: false, + ty: Array, 3>, + .. + }: ModuleIO { + name: sim_read_past::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ModuleIO { + name: sim_read_past::past_clocks, + is_input: false, + ty: Array, + .. + }: ModuleIO { + name: sim_read_past::past_clocks, + is_input: false, + ty: Array, + .. + }, + ModuleIO { + name: sim_read_past::past_outputs, + is_input: false, + ty: Array, 3>, + .. + }: ModuleIO { + name: sim_read_past::past_outputs, + is_input: false, + ty: Array, 3>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:6:1, + ), + }, + running_generator: Some( + ..., + ), + }, + ], + trace_decls: TraceModule { + name: "sim_read_past", + children: [ + TraceModuleIO { + name: "clocks", + child: TraceArray { + name: "clocks", + elements: [ + TraceClock { + location: TraceScalarId(0), + name: "[0]", + flow: Source, + }, + TraceClock { + location: TraceScalarId(1), + name: "[1]", + flow: Source, + }, + TraceClock { + location: TraceScalarId(2), + name: "[2]", + flow: Source, + }, + ], + ty: Array, + flow: Source, + }, + ty: Array, + flow: Source, + }, + TraceModuleIO { + name: "outputs", + child: TraceArray { + name: "outputs", + elements: [ + TraceUInt { + location: TraceScalarId(3), + name: "[0]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(4), + name: "[1]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(5), + name: "[2]", + ty: UInt<8>, + flow: Sink, + }, + ], + ty: Array, 3>, + flow: Sink, + }, + ty: Array, 3>, + flow: Sink, + }, + TraceModuleIO { + name: "past_clocks", + child: TraceArray { + name: "past_clocks", + elements: [ + TraceClock { + location: TraceScalarId(6), + name: "[0]", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(7), + name: "[1]", + flow: Sink, + }, + TraceClock { + location: TraceScalarId(8), + name: "[2]", + flow: Sink, + }, + ], + ty: Array, + flow: Sink, + }, + ty: Array, + flow: Sink, + }, + TraceModuleIO { + name: "past_outputs", + child: TraceArray { + name: "past_outputs", + elements: [ + TraceUInt { + location: TraceScalarId(9), + name: "[0]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(10), + name: "[1]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(11), + name: "[2]", + ty: UInt<8>, + flow: Sink, + }, + ], + ty: Array, 3>, + flow: Sink, + }, + ty: Array, 3>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigClock { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigClock { + index: StatePartIndex(2), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigUInt { + index: StatePartIndex(3), + ty: UInt<8>, + }, + state: 0x31, + last_state: 0x31, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigUInt { + index: StatePartIndex(4), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigUInt { + index: StatePartIndex(5), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + SimTrace { + id: TraceScalarId(6), + kind: BigClock { + index: StatePartIndex(6), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(7), + kind: BigClock { + index: StatePartIndex(7), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(8), + kind: BigClock { + index: StatePartIndex(8), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(9), + kind: BigUInt { + index: StatePartIndex(9), + ty: UInt<8>, + }, + state: 0x31, + last_state: 0x31, + }, + SimTrace { + id: TraceScalarId(10), + kind: BigUInt { + index: StatePartIndex(10), + ty: UInt<8>, + }, + state: 0x31, + last_state: 0x31, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigUInt { + index: StatePartIndex(11), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [ + StatePartIndex(1), + StatePartIndex(4), + StatePartIndex(7), + ], + event_queue: EventQueue(EventQueueData { + instant: 648 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 198, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_read_past: sim_read_past).sim_read_past::clocks[0]", + 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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 198, + .. + }, + }, + ), + }, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_read_past.vcd b/crates/fayalite/tests/sim/expected/sim_read_past.vcd new file mode 100644 index 00000000..5d0a932d --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_read_past.vcd @@ -0,0 +1,1908 @@ +$timescale 1 ps $end +$scope module sim_read_past $end +$scope struct clocks $end +$var wire 1 ! \[0] $end +$var wire 1 " \[1] $end +$var wire 1 # \[2] $end +$upscope $end +$scope struct outputs $end +$var wire 8 $ \[0] $end +$var wire 8 % \[1] $end +$var wire 8 & \[2] $end +$upscope $end +$scope struct past_clocks $end +$var wire 1 ' \[0] $end +$var wire 1 ( \[1] $end +$var wire 1 ) \[2] $end +$upscope $end +$scope struct past_outputs $end +$var wire 8 * \[0] $end +$var wire 8 + \[1] $end +$var wire 8 , \[2] $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +0" +0# +b0 $ +b0 % +b0 & +0' +0( +0) +b0 * +b0 + +b0 , +$end +#1000000 +1! +b1 $ +1' +#2000000 +0! +#3000000 +1! +#4000000 +0! +#5000000 +1! +#6000000 +0! +#7000000 +1! +#8000000 +0! +#9000000 +1! +#10000000 +0! +#11000000 +1! +#12000000 +0! +#13000000 +1! +#14000000 +0! +#15000000 +1" +b1 % +b1 * +0' +1( +#16000000 +0" +#17000000 +1! +#18000000 +0! +#19000000 +1! +#20000000 +0! +#21000000 +1! +#22000000 +0! +#23000000 +1# +b1 & +b1 + +0( +1) +#24000000 +0# +#25000000 +1! +b10 $ +b1 , +1' +0) +#26000000 +0! +#27000000 +1! +#28000000 +0! +#29000000 +1" +b10 % +b10 * +0' +1( +#30000000 +0" +#31000000 +1! +#32000000 +0! +#33000000 +1! +#34000000 +0! +#35000000 +1! +#36000000 +0! +#37000000 +1" +#38000000 +0" +#39000000 +1" +#40000000 +0" +#41000000 +1! +#42000000 +0! +#43000000 +1! +#44000000 +0! +#45000000 +1" +#46000000 +0" +#47000000 +1# +b10 & +b10 + +0( +1) +#48000000 +0# +#49000000 +1! +b11 $ +b10 , +1' +0) +#50000000 +0! +#51000000 +1! +#52000000 +0! +#53000000 +1# +b11 & +b11 * +0' +1) +#54000000 +0# +#55000000 +1! +#56000000 +0! +#57000000 +1! +#58000000 +0! +#59000000 +1! +#60000000 +0! +#61000000 +1# +#62000000 +0# +#63000000 +1" +b11 % +b11 , +1( +0) +#64000000 +0" +#65000000 +1! +b100 $ +b11 + +1' +0( +#66000000 +0! +#67000000 +1! +#68000000 +0! +#69000000 +1# +b100 & +b100 * +0' +1) +#70000000 +0# +#71000000 +1# +#72000000 +0# +#73000000 +1! +#74000000 +0! +#75000000 +1" +b100 % +b100 , +1( +0) +#76000000 +0" +#77000000 +1! +b101 $ +b100 + +1' +0( +#78000000 +0! +#79000000 +1! +#80000000 +0! +#81000000 +1! +#82000000 +0! +#83000000 +1" +b101 % +b101 * +0' +1( +#84000000 +0" +#85000000 +1! +#86000000 +0! +#87000000 +1" +#88000000 +0" +#89000000 +1! +#90000000 +0! +#91000000 +1" +#92000000 +0" +#93000000 +1! +#94000000 +0! +#95000000 +1# +b101 & +b101 + +0( +1) +#96000000 +0# +#97000000 +1! +b110 $ +b101 , +1' +0) +#98000000 +0! +#99000000 +1" +b110 % +b110 * +0' +1( +#100000000 +0" +#101000000 +1" +#102000000 +0" +#103000000 +1! +#104000000 +0! +#105000000 +1! +#106000000 +0! +#107000000 +1" +#108000000 +0" +#109000000 +1" +#110000000 +0" +#111000000 +1" +#112000000 +0" +#113000000 +1! +#114000000 +0! +#115000000 +1" +#116000000 +0" +#117000000 +1" +#118000000 +0" +#119000000 +1# +b110 & +b110 + +0( +1) +#120000000 +0# +#121000000 +1! +b111 $ +b110 , +1' +0) +#122000000 +0! +#123000000 +1" +b111 % +b111 * +0' +1( +#124000000 +0" +#125000000 +1# +b111 & +b111 + +0( +1) +#126000000 +0# +#127000000 +1! +b1000 $ +b111 , +1' +0) +#128000000 +0! +#129000000 +1! +#130000000 +0! +#131000000 +1" +b1000 % +b1000 * +0' +1( +#132000000 +0" +#133000000 +1# +b1000 & +b1000 + +0( +1) +#134000000 +0# +#135000000 +1" +b1001 % +b1000 , +1( +0) +#136000000 +0" +#137000000 +1! +b1001 $ +b1001 + +1' +0( +#138000000 +0! +#139000000 +1" +#140000000 +0" +#141000000 +1# +b1001 & +b1001 * +0' +1) +#142000000 +0# +#143000000 +1# +b1010 & +b1001 , +#144000000 +0# +#145000000 +1! +b1010 $ +b1010 , +1' +0) +#146000000 +0! +#147000000 +1# +#148000000 +0# +#149000000 +1! +#150000000 +0! +#151000000 +1! +#152000000 +0! +#153000000 +1! +#154000000 +0! +#155000000 +1# +#156000000 +0# +#157000000 +1! +#158000000 +0! +#159000000 +1" +b1010 % +b1010 * +0' +1( +#160000000 +0" +#161000000 +1! +b1011 $ +b1010 + +1' +0( +#162000000 +0! +#163000000 +1# +b1011 & +b1011 * +0' +1) +#164000000 +0# +#165000000 +1! +#166000000 +0! +#167000000 +1# +#168000000 +0# +#169000000 +1! +#170000000 +0! +#171000000 +1# +#172000000 +0# +#173000000 +1" +b1011 % +b1011 , +1( +0) +#174000000 +0" +#175000000 +1! +b1100 $ +b1011 + +1' +0( +#176000000 +0! +#177000000 +1! +#178000000 +0! +#179000000 +1# +b1100 & +b1100 * +0' +1) +#180000000 +0# +#181000000 +1" +b1100 % +b1100 , +1( +0) +#182000000 +0" +#183000000 +1" +b1101 % +b1100 + +#184000000 +0" +#185000000 +1! +b1101 $ +b1101 + +1' +0( +#186000000 +0! +#187000000 +1# +b1101 & +b1101 * +0' +1) +#188000000 +0# +#189000000 +1" +b1110 % +b1101 , +1( +0) +#190000000 +0" +#191000000 +1# +b1110 & +b1110 + +0( +1) +#192000000 +0# +#193000000 +1! +b1110 $ +b1110 , +1' +0) +#194000000 +0! +#195000000 +1# +b1111 & +b1110 * +0' +1) +#196000000 +0# +#197000000 +1# +#198000000 +0# +#199000000 +1! +b1111 $ +b1111 , +1' +0) +#200000000 +0! +#201000000 +1! +#202000000 +0! +#203000000 +1# +#204000000 +0# +#205000000 +1# +#206000000 +0# +#207000000 +1" +b1111 % +b1111 * +0' +1( +#208000000 +0" +#209000000 +1! +b10000 $ +b1111 + +1' +0( +#210000000 +0! +#211000000 +1# +b10000 & +b10000 * +0' +1) +#212000000 +0# +#213000000 +1# +#214000000 +0# +#215000000 +1# +#216000000 +0# +#217000000 +1" +b10000 % +b10000 , +1( +0) +#218000000 +0" +#219000000 +1! +b10001 $ +b10000 + +1' +0( +#220000000 +0! +#221000000 +1! +#222000000 +0! +#223000000 +1! +#224000000 +0! +#225000000 +1" +b10001 % +b10001 * +0' +1( +#226000000 +0" +#227000000 +1! +#228000000 +0! +#229000000 +1! +#230000000 +0! +#231000000 +1" +#232000000 +0" +#233000000 +1" +#234000000 +0" +#235000000 +1! +#236000000 +0! +#237000000 +1! +#238000000 +0! +#239000000 +1# +b10001 & +b10001 + +0( +1) +#240000000 +0# +#241000000 +1" +b10010 % +b10001 , +1( +0) +#242000000 +0" +#243000000 +1! +b10010 $ +b10010 + +1' +0( +#244000000 +0! +#245000000 +1" +#246000000 +0" +#247000000 +1! +#248000000 +0! +#249000000 +1" +#250000000 +0" +#251000000 +1! +#252000000 +0! +#253000000 +1" +#254000000 +0" +#255000000 +1" +#256000000 +0" +#257000000 +1" +#258000000 +0" +#259000000 +1! +#260000000 +0! +#261000000 +1" +#262000000 +0" +#263000000 +1# +b10010 & +b10010 * +0' +1) +#264000000 +0# +#265000000 +1" +b10011 % +b10010 , +1( +0) +#266000000 +0" +#267000000 +1! +b10011 $ +b10011 + +1' +0( +#268000000 +0! +#269000000 +1# +b10011 & +b10011 * +0' +1) +#270000000 +0# +#271000000 +1! +b10100 $ +b10011 , +1' +0) +#272000000 +0! +#273000000 +1" +b10100 % +b10100 * +0' +1( +#274000000 +0" +#275000000 +1! +#276000000 +0! +#277000000 +1# +b10100 & +b10100 + +0( +1) +#278000000 +0# +#279000000 +1" +b10101 % +b10100 , +1( +0) +#280000000 +0" +#281000000 +1" +#282000000 +0" +#283000000 +1! +b10101 $ +b10101 + +1' +0( +#284000000 +0! +#285000000 +1# +b10101 & +b10101 * +0' +1) +#286000000 +0# +#287000000 +1# +b10110 & +b10101 , +#288000000 +0# +#289000000 +1" +b10110 % +b10110 , +1( +0) +#290000000 +0" +#291000000 +1" +#292000000 +0" +#293000000 +1! +b10110 $ +b10110 + +1' +0( +#294000000 +0! +#295000000 +1! +b10111 $ +b10110 * +#296000000 +0! +#297000000 +1" +b10111 % +b10111 * +0' +1( +#298000000 +0" +#299000000 +1" +#300000000 +0" +#301000000 +1! +#302000000 +0! +#303000000 +1" +#304000000 +0" +#305000000 +1" +#306000000 +0" +#307000000 +1" +#308000000 +0" +#309000000 +1! +#310000000 +0! +#311000000 +1# +b10111 & +b10111 + +0( +1) +#312000000 +0# +#313000000 +1" +b11000 % +b10111 , +1( +0) +#314000000 +0" +#315000000 +1" +#316000000 +0" +#317000000 +1" +#318000000 +0" +#319000000 +1! +b11000 $ +b11000 + +1' +0( +#320000000 +0! +#321000000 +1" +#322000000 +0" +#323000000 +1" +#324000000 +0" +#325000000 +1" +#326000000 +0" +#327000000 +1" +#328000000 +0" +#329000000 +1" +#330000000 +0" +#331000000 +1" +#332000000 +0" +#333000000 +1" +#334000000 +0" +#335000000 +1# +b11000 & +b11000 * +0' +1) +#336000000 +0# +#337000000 +1" +b11001 % +b11000 , +1( +0) +#338000000 +0" +#339000000 +1" +#340000000 +0" +#341000000 +1# +b11001 & +b11001 + +0( +1) +#342000000 +0# +#343000000 +1! +b11001 $ +b11001 , +1' +0) +#344000000 +0! +#345000000 +1" +b11010 % +b11001 * +0' +1( +#346000000 +0" +#347000000 +1" +#348000000 +0" +#349000000 +1# +b11010 & +b11010 + +0( +1) +#350000000 +0# +#351000000 +1" +#352000000 +0" +#353000000 +1" +#354000000 +0" +#355000000 +1" +#356000000 +0" +#357000000 +1# +#358000000 +0# +#359000000 +1# +#360000000 +0# +#361000000 +1" +#362000000 +0" +#363000000 +1# +#364000000 +0# +#365000000 +1! +b11010 $ +b11010 , +1' +0) +#366000000 +0! +#367000000 +1! +b11011 $ +b11010 * +#368000000 +0! +#369000000 +1" +b11011 % +b11011 * +0' +1( +#370000000 +0" +#371000000 +1# +b11011 & +b11011 + +0( +1) +#372000000 +0# +#373000000 +1! +b11100 $ +b11011 , +1' +0) +#374000000 +0! +#375000000 +1" +b11100 % +b11100 * +0' +1( +#376000000 +0" +#377000000 +1" +#378000000 +0" +#379000000 +1# +b11100 & +b11100 + +0( +1) +#380000000 +0# +#381000000 +1! +b11101 $ +b11100 , +1' +0) +#382000000 +0! +#383000000 +1# +b11101 & +b11101 * +0' +1) +#384000000 +0# +#385000000 +1" +b11101 % +b11101 , +1( +0) +#386000000 +0" +#387000000 +1# +b11110 & +b11101 + +0( +1) +#388000000 +0# +#389000000 +1" +b11110 % +b11110 , +1( +0) +#390000000 +0" +#391000000 +1! +b11110 $ +b11110 + +1' +0( +#392000000 +0! +#393000000 +1" +b11111 % +b11110 * +0' +1( +#394000000 +0" +#395000000 +1# +b11111 & +b11111 + +0( +1) +#396000000 +0# +#397000000 +1" +#398000000 +0" +#399000000 +1" +#400000000 +0" +#401000000 +1" +#402000000 +0" +#403000000 +1# +#404000000 +0# +#405000000 +1" +#406000000 +0" +#407000000 +1# +#408000000 +0# +#409000000 +1" +#410000000 +0" +#411000000 +1# +#412000000 +0# +#413000000 +1# +#414000000 +0# +#415000000 +1! +b11111 $ +b11111 , +1' +0) +#416000000 +0! +#417000000 +1" +b100000 % +b11111 * +0' +1( +#418000000 +0" +#419000000 +1# +b100000 & +b100000 + +0( +1) +#420000000 +0# +#421000000 +1# +#422000000 +0# +#423000000 +1" +#424000000 +0" +#425000000 +1" +#426000000 +0" +#427000000 +1# +#428000000 +0# +#429000000 +1# +#430000000 +0# +#431000000 +1# +#432000000 +0# +#433000000 +1# +#434000000 +0# +#435000000 +1! +b100000 $ +b100000 , +1' +0) +#436000000 +0! +#437000000 +1! +b100001 $ +b100000 * +#438000000 +0! +#439000000 +1! +#440000000 +0! +#441000000 +1# +b100001 & +b100001 * +0' +1) +#442000000 +0# +#443000000 +1! +#444000000 +0! +#445000000 +1! +#446000000 +0! +#447000000 +1" +b100001 % +b100001 , +1( +0) +#448000000 +0" +#449000000 +1# +b100010 & +b100001 + +0( +1) +#450000000 +0# +#451000000 +1! +b100010 $ +b100010 , +1' +0) +#452000000 +0! +#453000000 +1! +#454000000 +0! +#455000000 +1# +#456000000 +0# +#457000000 +1# +#458000000 +0# +#459000000 +1! +#460000000 +0! +#461000000 +1" +b100010 % +b100010 * +0' +1( +#462000000 +0" +#463000000 +1! +b100011 $ +b100010 + +1' +0( +#464000000 +0! +#465000000 +1# +b100011 & +b100011 * +0' +1) +#466000000 +0# +#467000000 +1! +#468000000 +0! +#469000000 +1" +b100011 % +b100011 , +1( +0) +#470000000 +0" +#471000000 +1" +b100100 % +b100011 + +#472000000 +0" +#473000000 +1# +b100100 & +b100100 + +0( +1) +#474000000 +0# +#475000000 +1! +b100100 $ +b100100 , +1' +0) +#476000000 +0! +#477000000 +1" +b100101 % +b100100 * +0' +1( +#478000000 +0" +#479000000 +1# +b100101 & +b100101 + +0( +1) +#480000000 +0# +#481000000 +1# +#482000000 +0# +#483000000 +1! +b100101 $ +b100101 , +1' +0) +#484000000 +0! +#485000000 +1# +b100110 & +b100101 * +0' +1) +#486000000 +0# +#487000000 +1! +b100110 $ +b100110 , +1' +0) +#488000000 +0! +#489000000 +1# +#490000000 +0# +#491000000 +1! +#492000000 +0! +#493000000 +1# +#494000000 +0# +#495000000 +1" +b100110 % +b100110 * +0' +1( +#496000000 +0" +#497000000 +1# +b100111 & +b100110 + +0( +1) +#498000000 +0# +#499000000 +1! +b100111 $ +b100111 , +1' +0) +#500000000 +0! +#501000000 +1# +#502000000 +0# +#503000000 +1# +#504000000 +0# +#505000000 +1# +#506000000 +0# +#507000000 +1" +b100111 % +b100111 * +0' +1( +#508000000 +0" +#509000000 +1! +b101000 $ +b100111 + +1' +0( +#510000000 +0! +#511000000 +1! +#512000000 +0! +#513000000 +1# +b101000 & +b101000 * +0' +1) +#514000000 +0# +#515000000 +1" +b101000 % +b101000 , +1( +0) +#516000000 +0" +#517000000 +1! +b101001 $ +b101000 + +1' +0( +#518000000 +0! +#519000000 +1" +b101001 % +b101001 * +0' +1( +#520000000 +0" +#521000000 +1# +b101001 & +b101001 + +0( +1) +#522000000 +0# +#523000000 +1" +b101010 % +b101001 , +1( +0) +#524000000 +0" +#525000000 +1! +b101010 $ +b101010 + +1' +0( +#526000000 +0! +#527000000 +1# +b101010 & +b101010 * +0' +1) +#528000000 +0# +#529000000 +1# +b101011 & +b101010 , +#530000000 +0# +#531000000 +1" +b101011 % +b101011 , +1( +0) +#532000000 +0" +#533000000 +1" +#534000000 +0" +#535000000 +1! +b101011 $ +b101011 + +1' +0( +#536000000 +0! +#537000000 +1# +b101100 & +b101011 * +0' +1) +#538000000 +0# +#539000000 +1" +b101100 % +b101100 , +1( +0) +#540000000 +0" +#541000000 +1" +#542000000 +0" +#543000000 +1" +#544000000 +0" +#545000000 +1# +#546000000 +0# +#547000000 +1" +#548000000 +0" +#549000000 +1" +#550000000 +0" +#551000000 +1# +#552000000 +0# +#553000000 +1# +#554000000 +0# +#555000000 +1" +#556000000 +0" +#557000000 +1# +#558000000 +0# +#559000000 +1! +b101100 $ +b101100 + +1' +0( +#560000000 +0! +#561000000 +1# +b101101 & +b101100 * +0' +1) +#562000000 +0# +#563000000 +1" +b101101 % +b101101 , +1( +0) +#564000000 +0" +#565000000 +1# +#566000000 +0# +#567000000 +1" +#568000000 +0" +#569000000 +1# +#570000000 +0# +#571000000 +1" +#572000000 +0" +#573000000 +1# +#574000000 +0# +#575000000 +1# +#576000000 +0# +#577000000 +1# +#578000000 +0# +#579000000 +1# +#580000000 +0# +#581000000 +1! +b101101 $ +b101101 + +1' +0( +#582000000 +0! +#583000000 +1! +b101110 $ +b101101 * +#584000000 +0! +#585000000 +1# +b101110 & +b101110 * +0' +1) +#586000000 +0# +#587000000 +1# +#588000000 +0# +#589000000 +1! +#590000000 +0! +#591000000 +1" +b101110 % +b101110 , +1( +0) +#592000000 +0" +#593000000 +1# +b101111 & +b101110 + +0( +1) +#594000000 +0# +#595000000 +1# +#596000000 +0# +#597000000 +1! +b101111 $ +b101111 , +1' +0) +#598000000 +0! +#599000000 +1# +#600000000 +0# +#601000000 +1# +#602000000 +0# +#603000000 +1# +#604000000 +0# +#605000000 +1" +b101111 % +b101111 * +0' +1( +#606000000 +0" +#607000000 +1! +b110000 $ +b101111 + +1' +0( +#608000000 +0! +#609000000 +1# +b110000 & +b110000 * +0' +1) +#610000000 +0# +#611000000 +1# +#612000000 +0# +#613000000 +1" +b110000 % +b110000 , +1( +0) +#614000000 +0" +#615000000 +1" +b110001 % +b110000 + +#616000000 +0" +#617000000 +1# +b110001 & +b110001 + +0( +1) +#618000000 +0# +#619000000 +1# +#620000000 +0# +#621000000 +1" +#622000000 +0" +#623000000 +1# +#624000000 +0# +#625000000 +1# +#626000000 +0# +#627000000 +1# +#628000000 +0# +#629000000 +1# +#630000000 +0# +#631000000 +1! +b110001 $ +b110001 , +1' +0) +#632000000 +0! +#633000000 +1# +b110010 & +b110001 * +0' +1) +#634000000 +0# +#635000000 +1# +#636000000 +0# +#637000000 +1# +#638000000 +0# +#639000000 +1" +b110010 % +b110010 , +1( +0) +#640000000 +0" +#641000000 +1# +#642000000 +0# +#643000000 +1# +#644000000 +0# +#645000000 +1# +#646000000 +0# +#647000000 +1# +#648000000 +0# diff --git a/crates/fayalite/tests/sim/expected/sim_resettable_counter_async.txt b/crates/fayalite/tests/sim/expected/sim_resettable_counter_async.txt new file mode 100644 index 00000000..7b11157c --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_resettable_counter_async.txt @@ -0,0 +1,552 @@ +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(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.rst", + ty: AsyncReset, + }, + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::out", + 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, + 0, + 3, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd.clk, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd.rst, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.out, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }.clk, + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }.rst, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }: ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }: ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + }, + ], + trace_decls: TraceModule { + name: "sim_resettable_counter", + children: [ + TraceModuleIO { + name: "cd", + child: TraceBundle { + name: "cd", + fields: [ + TraceClock { + location: TraceScalarId(0), + name: "clk", + flow: Source, + }, + TraceAsyncReset { + location: TraceScalarId(1), + name: "rst", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + flow: Source, + }, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + flow: Source, + }, + TraceModuleIO { + name: "out", + child: TraceUInt { + location: TraceScalarId(2), + name: "out", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigAsyncReset { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 20 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 16, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: AsyncReset, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.rst", + ty: AsyncReset, + }, + ], + .. + }, + 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, + }: SimValue { + ty: AsyncReset, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + SensitivitySet { + id: 23, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: AsyncReset, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.rst", + ty: AsyncReset, + }, + ], + .. + }, + 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, + }: ( + SimValue { + ty: AsyncReset, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 16, + .. + }, + }, + ), + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 23, + .. + }, + }, + ), + }, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_resettable_counter_async.vcd b/crates/fayalite/tests/sim/expected/sim_resettable_counter_async.vcd new file mode 100644 index 00000000..f05658f4 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_resettable_counter_async.vcd @@ -0,0 +1,68 @@ +$timescale 1 ps $end +$scope module sim_resettable_counter $end +$scope struct cd $end +$var wire 1 ! clk $end +$var wire 1 " rst $end +$upscope $end +$var wire 8 # out $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +0" +b0 # +$end +#1000000 +1! +b1 # +#2000000 +0! +1" +b0 # +#3000000 +1! +#4000000 +0! +0" +#5000000 +1! +b1 # +#6000000 +0! +#7000000 +1! +b10 # +#8000000 +0! +#9000000 +1! +b11 # +#10000000 +0! +#11000000 +1! +b100 # +#12000000 +0! +1" +b0 # +#13000000 +1! +#14000000 +0! +0" +#15000000 +1! +b1 # +#16000000 +0! +#17000000 +1! +b10 # +#18000000 +0! +#19000000 +1! +b11 # +#20000000 +0! diff --git a/crates/fayalite/tests/sim/expected/sim_resettable_counter_async_immediate_reset.txt b/crates/fayalite/tests/sim/expected/sim_resettable_counter_async_immediate_reset.txt new file mode 100644 index 00000000..0ca767a7 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_resettable_counter_async_immediate_reset.txt @@ -0,0 +1,552 @@ +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(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.rst", + ty: AsyncReset, + }, + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::out", + 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, + 0, + 3, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd.clk, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd.rst, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.out, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }.clk, + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }.rst, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }: ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }: ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + }, + ], + trace_decls: TraceModule { + name: "sim_resettable_counter", + children: [ + TraceModuleIO { + name: "cd", + child: TraceBundle { + name: "cd", + fields: [ + TraceClock { + location: TraceScalarId(0), + name: "clk", + flow: Source, + }, + TraceAsyncReset { + location: TraceScalarId(1), + name: "rst", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + flow: Source, + }, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: AsyncReset, + }, + flow: Source, + }, + TraceModuleIO { + name: "out", + child: TraceUInt { + location: TraceScalarId(2), + name: "out", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigAsyncReset { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 20 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 13, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: AsyncReset, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.rst", + ty: AsyncReset, + }, + ], + .. + }, + 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, + }: SimValue { + ty: AsyncReset, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + SensitivitySet { + id: 20, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: AsyncReset, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.rst", + ty: AsyncReset, + }, + ], + .. + }, + 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, + }: ( + SimValue { + ty: AsyncReset, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 13, + .. + }, + }, + ), + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 20, + .. + }, + }, + ), + }, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_resettable_counter_async_immediate_reset.vcd b/crates/fayalite/tests/sim/expected/sim_resettable_counter_async_immediate_reset.vcd new file mode 100644 index 00000000..99f7b867 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_resettable_counter_async_immediate_reset.vcd @@ -0,0 +1,65 @@ +$timescale 1 ps $end +$scope module sim_resettable_counter $end +$scope struct cd $end +$var wire 1 ! clk $end +$var wire 1 " rst $end +$upscope $end +$var wire 8 # out $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +1" +b0 # +$end +#1000000 +1! +#2000000 +0! +#3000000 +1! +#4000000 +0! +0" +#5000000 +1! +b1 # +#6000000 +0! +#7000000 +1! +b10 # +#8000000 +0! +#9000000 +1! +b11 # +#10000000 +0! +#11000000 +1! +b100 # +#12000000 +0! +1" +b0 # +#13000000 +1! +#14000000 +0! +0" +#15000000 +1! +b1 # +#16000000 +0! +#17000000 +1! +b10 # +#18000000 +0! +#19000000 +1! +b11 # +#20000000 +0! diff --git a/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync.txt b/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync.txt new file mode 100644 index 00000000..4af2eb62 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync.txt @@ -0,0 +1,507 @@ +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(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.rst", + ty: SyncReset, + }, + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::out", + 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, + 0, + 3, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd.clk, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd.rst, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.out, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }.clk, + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }.rst, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }: ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }: ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + }, + ], + trace_decls: TraceModule { + name: "sim_resettable_counter", + 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: "out", + child: TraceUInt { + location: TraceScalarId(2), + name: "out", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigSyncReset { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 20 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 42, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + SensitivitySet { + id: 43, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 42, + .. + }, + SensitivitySet { + id: 43, + .. + }, + }, + ), + }, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync.vcd b/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync.vcd new file mode 100644 index 00000000..39c2641d --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync.vcd @@ -0,0 +1,70 @@ +$timescale 1 ps $end +$scope module sim_resettable_counter $end +$scope struct cd $end +$var wire 1 ! clk $end +$var wire 1 " rst $end +$upscope $end +$var wire 8 # out $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +0" +b0 # +$end +#1000000 +1! +b1 # +#2000000 +0! +1" +#3000000 +1! +b10 # +b0 # +#4000000 +0! +0" +#5000000 +1! +b1 # +#6000000 +0! +#7000000 +1! +b10 # +#8000000 +0! +#9000000 +1! +b11 # +#10000000 +0! +#11000000 +1! +b100 # +#12000000 +0! +1" +#13000000 +1! +b101 # +b0 # +#14000000 +0! +0" +#15000000 +1! +b1 # +#16000000 +0! +#17000000 +1! +b10 # +#18000000 +0! +#19000000 +1! +b11 # +#20000000 +0! diff --git a/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync_immediate_reset.txt b/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync_immediate_reset.txt new file mode 100644 index 00000000..45f09d20 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync_immediate_reset.txt @@ -0,0 +1,507 @@ +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(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.rst", + ty: SyncReset, + }, + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::out", + 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, + 0, + 3, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd.clk, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.cd.rst, + Instance { + name: ::sim_resettable_counter, + instantiated: Module { + name: sim_resettable_counter, + .. + }, + }.out, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }.clk, + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }.rst, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + }, + did_initial_settle: true, + clocks_for_past: {}, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }: ModuleIO { + name: sim_resettable_counter::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }, + ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }: ModuleIO { + name: sim_resettable_counter::out, + is_input: false, + ty: UInt<8>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + }, + ], + trace_decls: TraceModule { + name: "sim_resettable_counter", + 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: "out", + child: TraceUInt { + location: TraceScalarId(2), + name: "out", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigSyncReset { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 20 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 43, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + SensitivitySet { + id: 44, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_resettable_counter: sim_resettable_counter).sim_resettable_counter::cd.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: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 43, + .. + }, + SensitivitySet { + id: 44, + .. + }, + }, + ), + }, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync_immediate_reset.vcd b/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync_immediate_reset.vcd new file mode 100644 index 00000000..3cb97e24 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_resettable_counter_sync_immediate_reset.vcd @@ -0,0 +1,70 @@ +$timescale 1 ps $end +$scope module sim_resettable_counter $end +$scope struct cd $end +$var wire 1 ! clk $end +$var wire 1 " rst $end +$upscope $end +$var wire 8 # out $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +1" +b0 # +$end +#1000000 +1! +b1 # +b0 # +#2000000 +0! +#3000000 +1! +b1 # +b0 # +#4000000 +0! +0" +#5000000 +1! +b1 # +#6000000 +0! +#7000000 +1! +b10 # +#8000000 +0! +#9000000 +1! +b11 # +#10000000 +0! +#11000000 +1! +b100 # +#12000000 +0! +1" +#13000000 +1! +b101 # +b0 # +#14000000 +0! +0" +#15000000 +1! +b1 # +#16000000 +0! +#17000000 +1! +b10 # +#18000000 +0! +#19000000 +1! +b11 # +#20000000 +0! diff --git a/crates/fayalite/tests/ui/module.rs b/crates/fayalite/tests/ui/module.rs index 36649aa2..ee0f9887 100644 --- a/crates/fayalite/tests/ui/module.rs +++ b/crates/fayalite/tests/ui/module.rs @@ -11,4 +11,20 @@ pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) { let o: UInt<8> = m.output(); } +#[hdl_module] +pub fn my_module2(platform_io_builder: PlatformIOBuilder<'_>) { + #[hdl] + let a: UInt<8> = m.input(); + #[hdl] + let b: UInt<8> = m.output(); + #[hdl] + let io = m.add_platform_io(platform_io_builder); + #[hdl] + let c: UInt<8> = m.input(); + #[hdl] + let d: UInt<8> = m.output(); + #[hdl] + let io = m.add_platform_io(platform_io_builder); +} + fn main() {} diff --git a/crates/fayalite/tests/ui/module.stderr b/crates/fayalite/tests/ui/module.stderr index efbd1fea..979e76fe 100644 --- a/crates/fayalite/tests/ui/module.stderr +++ b/crates/fayalite/tests/ui/module.stderr @@ -1,17 +1,47 @@ -error: name conflicts with implicit `m: &mut ModuleBuilder<_>` +error: name conflicts with implicit `m: &ModuleBuilder` --> tests/ui/module.rs:7:26 | 7 | pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) { | ^ -error: name conflicts with implicit `m: &mut ModuleBuilder<_>` +error: name conflicts with implicit `m: &ModuleBuilder` --> tests/ui/module.rs:7:35 | 7 | pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) { | ^ -error: name conflicts with implicit `m: &mut ModuleBuilder<_>` +error: name conflicts with implicit `m: &ModuleBuilder` --> tests/ui/module.rs:9:9 | 9 | let m: UInt<8> = m.input(); | ^ + +error: can't have other inputs/outputs in a module using m.add_platform_io() + --> tests/ui/module.rs:17:24 + | +17 | let a: UInt<8> = m.input(); + | ^^^^^ + +error: can't have other inputs/outputs in a module using m.add_platform_io() + --> tests/ui/module.rs:19:24 + | +19 | let b: UInt<8> = m.output(); + | ^^^^^^ + +error: can't have other inputs/outputs in a module using m.add_platform_io() + --> tests/ui/module.rs:23:24 + | +23 | let c: UInt<8> = m.input(); + | ^^^^^ + +error: can't have other inputs/outputs in a module using m.add_platform_io() + --> tests/ui/module.rs:25:24 + | +25 | let d: UInt<8> = m.output(); + | ^^^^^^ + +error: can't use m.add_platform_io() more than once in a single module + --> tests/ui/module.rs:27:16 + | +27 | let io = m.add_platform_io(platform_io_builder); + | ^^^^^^^^^^^^^^^ diff --git a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr index 03c62bf6..7191e5e9 100644 --- a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr +++ b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr @@ -4,14 +4,14 @@ error[E0277]: `Cell` cannot be shared between thr 11 | fn f(v: SimValue<()>) -> Interned> { | ^^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = help: within `fayalite::prelude::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<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -28,13 +28,13 @@ error[E0277]: `UnsafeCell>` cannot be shared between th 11 | fn f(v: SimValue<()>) -> Interned> { | ^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell>` cannot be shared between threads safely | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` + = help: within `fayalite::prelude::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<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -51,7 +51,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta 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)>` + = help: within `fayalite::prelude::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 | @@ -92,7 +92,7 @@ note: required because it appears within the type `util::alternating_cell::Alter | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -103,29 +103,15 @@ note: required by a bound in `fayalite::intern::Interned` | pub struct Interned { | ^^^^ required by this bound in `Interned` -error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied +error[E0277]: the trait bound `fayalite::prelude::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<()>` + | -------------------- ^ the trait `Hash` is not implemented for `fayalite::prelude::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` + = note: required for `fayalite::prelude::SimValue<()>` to implement `Intern` help: consider dereferencing here | 12 | Intern::intern_sized(*v) @@ -139,14 +125,14 @@ error[E0277]: `Cell` cannot be shared between thr | | | required by a bound introduced by this call | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = help: within `fayalite::prelude::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<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -156,7 +142,7 @@ note: required by a bound in `intern_sized` | | 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 @@ -172,13 +158,13 @@ error[E0277]: `UnsafeCell>` cannot be shared between th | | | required by a bound introduced by this call | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` + = help: within `fayalite::prelude::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<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -188,7 +174,7 @@ note: required by a bound in `intern_sized` | | 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 @@ -204,7 +190,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta | | | 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)>` + = help: within `fayalite::prelude::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 | @@ -245,7 +231,7 @@ note: required because it appears within the type `util::alternating_cell::Alter | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -255,7 +241,7 @@ note: required by a bound in `intern_sized` | | 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 @@ -269,14 +255,14 @@ error[E0277]: `Cell` cannot be shared between thr 12 | Intern::intern_sized(v) | ^^^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = help: within `fayalite::prelude::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<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -293,13 +279,13 @@ error[E0277]: `UnsafeCell>` cannot be shared between th 12 | Intern::intern_sized(v) | ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell>` cannot be shared between threads safely | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` + = help: within `fayalite::prelude::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<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -316,7 +302,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta 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)>` + = help: within `fayalite::prelude::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 | @@ -357,7 +343,7 @@ note: required because it appears within the type `util::alternating_cell::Alter | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index effbd827..a74cef91 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -162,6 +162,7 @@ "$kind": "Struct", "verilog_name": "Visible", "parameters": "Visible", + "clocks_for_past": "Visible", "simulation": "Visible" } }, @@ -1176,7 +1177,8 @@ "BlackBoxInline": "Visible", "BlackBoxPath": "Visible", "DocString": "Visible", - "CustomFirrtl": "Visible" + "CustomFirrtl": "Visible", + "Xilinx": "Visible" } }, "DontTouchAnnotation": { @@ -1214,6 +1216,29 @@ "$kind": "Opaque" } }, + "XilinxAnnotation": { + "data": { + "$kind": "Enum", + "XdcLocation": "Visible", + "XdcIOStandard": "Visible", + "XdcCreateClock": "Visible" + } + }, + "XdcLocationAnnotation": { + "data": { + "$kind": "Opaque" + } + }, + "XdcIOStandardAnnotation": { + "data": { + "$kind": "Opaque" + } + }, + "XdcCreateClockAnnotation": { + "data": { + "$kind": "Opaque" + } + }, "Target": { "data": { "$kind": "Enum", diff --git a/rocq-demo/rocq_hdl.v b/rocq-demo/rocq_hdl.v new file mode 100644 index 00000000..74082261 --- /dev/null +++ b/rocq-demo/rocq_hdl.v @@ -0,0 +1,171 @@ +(* SPDX-License-Identifier: LGPL-3.0-or-later + See Notices.txt for copyright information *) + +(* This file demonstrates converting simple HDL designs into Rocq, as well + as proofs by induction that parallels those of formal verification in + Symbiyosys with the "smtbmc" engine in the "prove" mode. *) + +(* Models a simple register, feeding back to itself. We prove that, + when initialized to zero, it stays always at zero. *) +Module simple_register. + + (* State record. Contains only one boolean variable, which we call "a" *) + Record S := { a: bool }. + (* The above automatically defines an accessor function "a: S->bool", so + we can write "a s" to get the value of "a" in state "s". *) + + (* Defines an "initial" predicate, so "initial s" means + "s is an initial state". In our case, the value of "a" + in the initial state is zero (false) *) + Definition initial (s: S) := + a s = false. + (* As an example, we construct a state where "a" is indeed false, + and prove it is an initial state. The syntax for instantiating + such a record is "{| a := false |}". *) + Example test_initial: initial {| a := false |}. + Proof. reflexivity. Qed. + + (* Defines an "assert" predicate, so "assert s" means "assertions + hold in state s" or equivalently "state s is valid". In our case, + valid states correspond to those where "a" is false. *) + Definition assert (s: S) := + a s = false. + + (* The following proof by induction follows the article "Temporal + Induction by Incremental SAT Solving", + https://doi.org/10.1016/S1571-0661(05)82542-3 *) + (* Proves the base case, with one step: the initial state is a + valid state. *) + Theorem base1: forall s0, initial s0 -> assert s0. + Proof. trivial. Qed. + + (* Defines a "transition" predicate, so "t s1 s2" means: + "s2 can be reached in one step from s1". In our case, + the value of "a" in s2 is the same as in s1. *) + Definition t (s1 s2 : S) := + a s2 = a s1. + + (* Induction principle: for all transitions from a valid state, + we reach a valid state. *) + Theorem induct1: + forall s0 s1, assert s0 -> t s0 s1 -> assert s1. + Proof. + unfold t, assert. + intros s0 s1 A T. + transitivity (a s0). + apply T. apply A. + Qed. + (* Here we have proven the base case and the inductive case, so we + declare success. However, we may want to formalize it from first + principles, something like "All reacheable states from an initial + state are valid", and use the above theorems to prove it (TODO). *) + + (* Example: all states reacheable in one step from the initial state + are valid. *) + Example one_step_valid: + forall s0 s1, initial s0 -> t s0 s1 -> assert s1. + Proof. + unfold initial, t, assert. + intros s0 s1 I T. + transitivity (a s0). + apply T. apply I. + Qed. + (* Same as above, but we prove using only the induction theorems. *) + Example one_step_valid': + forall s0 s1, initial s0 -> t s0 s1 -> assert s1. + Proof. + apply induct1. + Qed. +End simple_register. + +(* The same circuit as above, but with an additional output register. + The output is asserted to be always false, but no such restriction + is made on the inner register, a "hidden" state. It creates a + difficulty for induction: there are unreacheable valid states that + leads to invalid states. A two step induction solves the problem. *) +Module hidden_state. + (* Demonstrate state with several (two) variables. *) + Record S := + { a: bool + ; b: bool + }. + (* Both registers start at zero (false). *) + Definition initial (s: S) := + a s = false /\ b s = false. + (* Just b is constrained to be zero (false) *) + Definition assert (s: S) := + b s = false. + (* "a" keeps its previous value, "b" copies "a". *) + Definition t (s1 s2 : S) := + a s2 = a s1 /\ b s2 = a s1. + (* Base case in one step. *) + Theorem base1: + forall s0, + initial s0 -> assert s0. + Proof. + unfold initial, assert. + intros s0 [_ H]. + apply H. + Qed. + (* Try proving the inductive case in one step. *) + Theorem induct1: + forall s0 s1, + assert s0 -> t s0 s1 -> assert s1. + Proof. + unfold t, assert. + intros s0 s1 A T. + destruct T as [_ Tb]. + transitivity (a s0). apply Tb. + Abort. + (* Ops, we reach a dead-end. Maybe it's not provable after all? + Try proving its negation by finding a counter-example. *) + Theorem not_induct1: + not (forall s0 s1, + assert s0 -> t s0 s1 -> assert s1). + Proof. + unfold not, assert, t. + intros H. + specialize H with (s0 := {|a := true; b := false|}). + simpl in H. + specialize H with (s1 := {|a := true; b := true|}). + simpl in H. + discriminate H. reflexivity. auto. + Qed. + (* Indeed, a valid state where "a" is one (true) leads to + an invalid state. Try one more step. *) + Theorem base2: + forall s0 s1, + initial s0 -> assert s0 -> t s0 s1 -> assert s1. + Proof. + intros s0 s1 [Ia _] _ [_ Tb]. + unfold assert. + transitivity (a s0). apply Tb. + apply Ia. + Qed. + Theorem induct2: + forall s0 s1 s2, + assert s0 -> t s0 s1 -> assert s1 -> t s1 s2 -> assert s2. + Proof. + intros s0 s1 s2 A0 [T0a T0b] A1 [T1a T1b]. + unfold assert in *. + transitivity (a s1). apply T1b. + transitivity (a s0). apply T0a. + transitivity (b s1). symmetry. apply T0b. + apply A1. + Qed. + (* Success! Two steps indeed work. *) + + (* Example: all states reacheable in two steps are valid *) + Example two_steps_valid: + forall s0 s1 s2, + initial s0 -> t s0 s1 -> t s1 s2 -> assert s2. + Proof. + intros s0 s1 s2. + intros Hi T01. + apply induct2 with (s0 := s0). + - apply base1. apply Hi. + - apply T01. + - apply base2 with (s0 := s0). + apply Hi. apply base1. apply Hi. apply T01. + Qed. +End hidden_state. diff --git a/scripts/check-copyright.sh b/scripts/check-copyright.sh index 3651931f..023cd21f 100755 --- a/scripts/check-copyright.sh +++ b/scripts/check-copyright.sh @@ -32,6 +32,7 @@ POUND_HEADER=('^"# SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"# See Notice SLASH_HEADER=('^"// SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"// See Notices.txt for copyright information"$') MD_HEADER=('^"