Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

196 changed files with 9867 additions and 147653 deletions

View file

@ -0,0 +1,77 @@
# 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 }}

View file

@ -3,26 +3,62 @@
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
deps:
runs-on: debian-12
uses: ./.forgejo/workflows/deps.yml
test: test:
runs-on: debian-12 runs-on: debian-12
container: needs: deps
image: git.libre-chip.org/libre-chip/fayalite-deps:latest
steps: steps:
- uses: actions/checkout@v3 - uses: https://git.libre-chip.org/mirrors/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- run: | - run: |
scripts/check-copyright.sh 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 - uses: https://git.libre-chip.org/mirrors/rust-cache@v2
with: with:
save-if: ${{ github.ref == 'refs/heads/master' }} save-if: ${{ github.ref == 'refs/heads/master' }}
- run: rustup override set 1.93.0
- run: rustup component add rust-src
- run: make -C rocq-demo
- run: cargo test - run: cargo test
- run: cargo build --tests --features=unstable-doc - run: cargo build --tests --features=unstable-doc
- run: cargo test --doc --features=unstable-doc - run: cargo test --doc --features=unstable-doc
- run: cargo doc --features=unstable-doc - run: cargo doc --features=unstable-doc
- run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher - 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

132
Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 3
[[package]] [[package]]
name = "allocator-api2" name = "allocator-api2"
@ -25,9 +25,9 @@ dependencies = [
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.13" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
@ -81,12 +81,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "basic-toml" name = "basic-toml"
version = "0.1.8" version = "0.1.8"
@ -155,9 +149,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.48" version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -165,9 +159,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.48" version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -175,20 +169,11 @@ dependencies = [
"strsim", "strsim",
] ]
[[package]]
name = "clap_complete"
version = "4.5.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75bf0b32ad2e152de789bb635ea4d3078f6b838ad7974143e99b99f45a04af4a"
dependencies = [
"clap",
]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.47" version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -198,9 +183,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.5" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
@ -306,11 +291,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
name = "fayalite" name = "fayalite"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"base64",
"bitvec", "bitvec",
"blake3", "blake3",
"clap", "clap",
"clap_complete",
"ctor", "ctor",
"eyre", "eyre",
"fayalite-proc-macros", "fayalite-proc-macros",
@ -319,12 +302,10 @@ dependencies = [
"jobslot", "jobslot",
"num-bigint", "num-bigint",
"num-traits", "num-traits",
"once_cell", "os_pipe",
"ordered-float",
"petgraph", "petgraph",
"serde", "serde",
"serde_json", "serde_json",
"sha2",
"tempfile", "tempfile",
"trybuild", "trybuild",
"vec_map", "vec_map",
@ -396,13 +377,12 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.3.3" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi",
"wasi", "wasi",
] ]
@ -469,23 +449,23 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]] [[package]]
name = "jobslot" name = "jobslot"
version = "0.2.23" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c" checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"derive_destructure2", "derive_destructure2",
"getrandom", "getrandom",
"libc", "libc",
"scopeguard", "scopeguard",
"windows-sys 0.61.2", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.176" version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
@ -523,19 +503,18 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "ordered-float" name = "os_pipe"
version = "5.1.0" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [ dependencies = [
"num-traits", "libc",
"rand", "windows-sys 0.59.0",
"serde",
] ]
[[package]] [[package]]
@ -578,37 +557,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]] [[package]]
name = "radium" name = "radium"
version = "0.7.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 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]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.31" version = "0.38.31"
@ -794,21 +748,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.14.7+wasi-0.2.4" version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
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]] [[package]]
name = "which" name = "which"
@ -853,12 +795,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -870,11 +806,11 @@ dependencies = [
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.61.2" version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [ dependencies = [
"windows-link", "windows-targets",
] ]
[[package]] [[package]]
@ -947,12 +883,6 @@ version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]] [[package]]
name = "wyz" name = "wyz"
version = "0.5.1" version = "0.5.1"

View file

@ -11,27 +11,24 @@ edition = "2024"
repository = "https://git.libre-chip.org/libre-chip/fayalite" repository = "https://git.libre-chip.org/libre-chip/fayalite"
keywords = ["hdl", "hardware", "semiconductors", "firrtl", "fpga"] keywords = ["hdl", "hardware", "semiconductors", "firrtl", "fpga"]
categories = ["simulation", "development-tools", "compilers"] categories = ["simulation", "development-tools", "compilers"]
rust-version = "1.93.0" rust-version = "1.89.0"
[workspace.dependencies] [workspace.dependencies]
fayalite-proc-macros = { version = "=0.3.0", path = "crates/fayalite-proc-macros" } 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-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" } fayalite-visit-gen = { version = "=0.3.0", path = "crates/fayalite-visit-gen" }
base16ct = "0.2.0" base16ct = "0.2.0"
base64 = "0.22.1"
bitvec = { version = "1.0.1", features = ["serde"] } bitvec = { version = "1.0.1", features = ["serde"] }
blake3 = { version = "1.5.4", features = ["serde"] } blake3 = { version = "1.5.4", features = ["serde"] }
clap = { version = "4.5.9", features = ["derive", "env", "string"] } clap = { version = "4.5.9", features = ["derive", "env", "string"] }
clap_complete = "4.5.58"
ctor = "0.2.8" ctor = "0.2.8"
eyre = "0.6.12" eyre = "0.6.12"
hashbrown = "0.15.2" hashbrown = "0.15.2"
indexmap = { version = "2.5.0", features = ["serde"] } indexmap = { version = "2.5.0", features = ["serde"] }
jobslot = "0.2.23" jobslot = "0.2.19"
num-bigint = "0.4.6" num-bigint = "0.4.6"
num-traits = "0.2.16" num-traits = "0.2.16"
once_cell = "1.21.3" os_pipe = "1.2.1"
ordered-float = { version = "5.1.0", features = ["serde"] }
petgraph = "0.8.1" petgraph = "0.8.1"
prettyplease = "0.2.20" prettyplease = "0.2.20"
proc-macro2 = "1.0.83" proc-macro2 = "1.0.83"

View file

@ -8,73 +8,6 @@ Fayalite is a library for designing digital hardware -- a hardware description l
[FIRRTL]: https://github.com/chipsalliance/firrtl-spec [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 <https://git.libre-chip.org/libre-chip/fayalite-deps>
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 # Funding
## NLnet Grants ## NLnet Grants

View file

@ -257,6 +257,5 @@ no_op_fold!(syn::Token![let]);
no_op_fold!(syn::Token![mut]); no_op_fold!(syn::Token![mut]);
no_op_fold!(syn::Token![static]); no_op_fold!(syn::Token![static]);
no_op_fold!(syn::Token![struct]); no_op_fold!(syn::Token![struct]);
no_op_fold!(syn::Token![type]);
no_op_fold!(syn::Token![where]); no_op_fold!(syn::Token![where]);
no_op_fold!(usize); no_op_fold!(usize);

View file

@ -3,9 +3,8 @@
use crate::{ use crate::{
Errors, HdlAttr, PairsIterExt, Errors, HdlAttr, PairsIterExt,
hdl_type_common::{ hdl_type_common::{
CustomDebugOptions, CustomDebugTrait, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, ParsedFieldsNamed, ParsedGenerics,
ParsedField, ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst, SplitForImpl, TypesParser, WrappedInConst, common_derives, get_target,
common_derives, create_struct_debug_impl, get_target,
}, },
kw, kw,
}; };
@ -31,7 +30,6 @@ pub(crate) struct ParsedBundle {
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>, pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>, pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>,
pub(crate) mask_type_ident: Ident, pub(crate) mask_type_ident: Ident,
pub(crate) mask_type_name: String,
pub(crate) mask_type_match_variant_ident: Ident, pub(crate) mask_type_match_variant_ident: Ident,
pub(crate) mask_type_sim_value_ident: Ident, pub(crate) mask_type_sim_value_ident: Ident,
pub(crate) match_variant_ident: Ident, pub(crate) match_variant_ident: Ident,
@ -89,13 +87,7 @@ impl ParsedBundle {
no_static: _, no_static: _,
no_runtime_generics: _, no_runtime_generics: _,
cmp_eq: _, cmp_eq: _,
ref get,
custom_debug: _,
custom_sim_display: _,
} = options.body; } = options.body;
if let Some((get, ..)) = get {
errors.error(get, "#[hdl(get(...))] is not allowed on structs");
}
let mut fields = match fields { let mut fields = match fields {
syn::Fields::Named(fields) => fields, syn::Fields::Named(fields) => fields,
syn::Fields::Unnamed(fields) => { syn::Fields::Unnamed(fields) => {
@ -135,7 +127,6 @@ impl ParsedBundle {
fields, fields,
field_flips, field_flips,
mask_type_ident: format_ident!("__{}__MaskType", ident), mask_type_ident: format_ident!("__{}__MaskType", ident),
mask_type_name: format!("MaskType<{}>", ident),
mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident), mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident),
mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident), mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident),
match_variant_ident: format_ident!("__{}__MatchVariant", ident), match_variant_ident: format_ident!("__{}__MatchVariant", ident),
@ -229,7 +220,7 @@ impl Builder {
.args .args
.push_value(match get_field_state(field_index) { .push_value(match get_field_state(field_index) {
BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=> BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=>
() ::fayalite::bundle::Unfilled<#ty>
}, },
BuilderFieldState::Generic => { BuilderFieldState::Generic => {
let type_var = type_var_for_field_name(ident); let type_var = type_var_for_field_name(ident);
@ -354,6 +345,7 @@ impl ToTokens for Builder {
} }
})); }));
quote_spanned! {self.ident.span()=> quote_spanned! {self.ident.span()=>
#[automatically_derived]
#[allow(non_camel_case_types, non_snake_case, dead_code)] #[allow(non_camel_case_types, non_snake_case, dead_code)]
impl #impl_generics #unfilled_ty impl #impl_generics #unfilled_ty
#where_clause #where_clause
@ -388,7 +380,7 @@ impl ToTokens for Builder {
fn default() -> Self { fn default() -> Self {
#ident { #ident {
#phantom_field_name: ::fayalite::__std::marker::PhantomData, #phantom_field_name: ::fayalite::__std::marker::PhantomData,
#(#field_idents: (),)* #(#field_idents: ::fayalite::__std::default::Default::default(),)*
} }
} }
} }
@ -400,30 +392,16 @@ impl ToTokens for Builder {
let type_generics = self.generics.split_for_impl().1; let type_generics = self.generics.split_for_impl().1;
quote_spanned! {self.ident.span()=> quote_spanned! {self.ident.span()=>
#[automatically_derived] #[automatically_derived]
#[allow(non_camel_case_types, dead_code, private_interfaces)] #[allow(non_camel_case_types, dead_code)]
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) -> <Self as ::fayalite::expr::ValueType>::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 impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
#filled_where_clause #filled_where_clause
{ {
type Type = #target #type_generics;
fn to_expr( fn to_expr(
&self, &self,
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ValueType>::Type> { ) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> {
let __ty = #target { let __ty = #target {
#(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)* #(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)*
}; };
let __field_values = [ let __field_values = [
#(::fayalite::expr::Expr::canonical(self.#field_idents),)* #(::fayalite::expr::Expr::canonical(self.#field_idents),)*
@ -453,7 +431,6 @@ impl ToTokens for ParsedBundle {
fields, fields,
field_flips, field_flips,
mask_type_ident, mask_type_ident,
mask_type_name,
mask_type_match_variant_ident, mask_type_match_variant_ident,
mask_type_sim_value_ident, mask_type_sim_value_ident,
match_variant_ident, match_variant_ident,
@ -469,21 +446,11 @@ impl ToTokens for ParsedBundle {
no_static, no_static,
no_runtime_generics, no_runtime_generics,
cmp_eq, cmp_eq,
get: _,
custom_debug: _,
custom_sim_display,
} = &options.body; } = &options.body;
let CustomDebugOptions {
type_: custom_debug_type,
sim: custom_debug_sim,
mask_type: custom_debug_mask_type,
mask_sim: custom_debug_mask_sim,
} = options.body.custom_debug();
let target = get_target(target, ident); let target = get_target(target, ident);
let struct_name = ident.to_string();
let mut item_attrs = attrs.clone(); let mut item_attrs = attrs.clone();
item_attrs.push(common_derives(span, false)); item_attrs.push(common_derives(span));
let type_struct = ItemStruct { ItemStruct {
attrs: item_attrs, attrs: item_attrs,
vis: vis.clone(), vis: vis.clone(),
struct_token: *struct_token, struct_token: *struct_token,
@ -491,8 +458,8 @@ impl ToTokens for ParsedBundle {
generics: generics.into(), generics: generics.into(),
fields: Fields::Named(fields.clone().into()), fields: Fields::Named(fields.clone().into()),
semi_token: None, semi_token: None,
}; }
type_struct.to_tokens(tokens); .to_tokens(tokens);
let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields), None) = if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields), None) =
(generics, fields, no_runtime_generics) (generics, fields, no_runtime_generics)
@ -518,9 +485,6 @@ impl ToTokens for ParsedBundle {
} }
let mut wrapped_in_const = WrappedInConst::new(tokens, span); let mut wrapped_in_const = WrappedInConst::new(tokens, span);
let tokens = wrapped_in_const.inner(); let tokens = wrapped_in_const.inner();
if custom_debug_type.is_none() {
create_struct_debug_impl(&type_struct, &struct_name, None).to_tokens(tokens);
}
let builder = Builder { let builder = Builder {
vis: vis.clone(), vis: vis.clone(),
struct_token: *struct_token, struct_token: *struct_token,
@ -531,6 +495,7 @@ impl ToTokens for ParsedBundle {
}; };
builder.to_tokens(tokens); builder.to_tokens(tokens);
let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); 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()); let mut mask_type_fields = FieldsNamed::from(fields.clone());
for Field { ty, .. } in &mut mask_type_fields.named { for Field { ty, .. } in &mut mask_type_fields.named {
*ty = parse_quote_spanned! {span=> *ty = parse_quote_spanned! {span=>
@ -548,9 +513,11 @@ impl ToTokens for ParsedBundle {
mask_type_builder.to_tokens(tokens); mask_type_builder.to_tokens(tokens);
let unfilled_mask_type_builder_ty = let unfilled_mask_type_builder_ty =
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
let mask_type_struct = ItemStruct { let filled_mask_type_builder_ty =
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled);
ItemStruct {
attrs: vec![ attrs: vec![
common_derives(span, false), common_derives(span),
parse_quote_spanned! {span=> parse_quote_spanned! {span=>
#[allow(non_camel_case_types, dead_code)] #[allow(non_camel_case_types, dead_code)]
}, },
@ -561,20 +528,17 @@ impl ToTokens for ParsedBundle {
generics: generics.into(), generics: generics.into(),
fields: Fields::Named(mask_type_fields.clone()), fields: Fields::Named(mask_type_fields.clone()),
semi_token: None, semi_token: None,
};
mask_type_struct.to_tokens(tokens);
if custom_debug_mask_type.is_none() {
create_struct_debug_impl(&mask_type_struct, mask_type_name, None).to_tokens(tokens);
} }
.to_tokens(tokens);
let mut mask_type_match_variant_fields = mask_type_fields.clone(); let mut mask_type_match_variant_fields = mask_type_fields.clone();
for Field { ty, .. } in &mut mask_type_match_variant_fields.named { for Field { ty, .. } in &mut mask_type_match_variant_fields.named {
*ty = parse_quote_spanned! {span=> *ty = parse_quote_spanned! {span=>
::fayalite::expr::Expr<#ty> ::fayalite::expr::Expr<#ty>
}; };
} }
let mask_type_match_variant_struct = ItemStruct { ItemStruct {
attrs: vec![ attrs: vec![
common_derives(span, false), common_derives(span),
parse_quote_spanned! {span=> parse_quote_spanned! {span=>
#[allow(non_camel_case_types, dead_code)] #[allow(non_camel_case_types, dead_code)]
}, },
@ -585,9 +549,7 @@ impl ToTokens for ParsedBundle {
generics: generics.into(), generics: generics.into(),
fields: Fields::Named(mask_type_match_variant_fields), fields: Fields::Named(mask_type_match_variant_fields),
semi_token: None, semi_token: None,
}; }
mask_type_match_variant_struct.to_tokens(tokens);
create_struct_debug_impl(&mask_type_match_variant_struct, mask_type_name, None)
.to_tokens(tokens); .to_tokens(tokens);
let mut match_variant_fields = FieldsNamed::from(fields.clone()); let mut match_variant_fields = FieldsNamed::from(fields.clone());
for Field { ty, .. } in &mut match_variant_fields.named { for Field { ty, .. } in &mut match_variant_fields.named {
@ -595,9 +557,9 @@ impl ToTokens for ParsedBundle {
::fayalite::expr::Expr<#ty> ::fayalite::expr::Expr<#ty>
}; };
} }
let match_variant_struct = ItemStruct { ItemStruct {
attrs: vec![ attrs: vec![
common_derives(span, false), common_derives(span),
parse_quote_spanned! {span=> parse_quote_spanned! {span=>
#[allow(non_camel_case_types, dead_code)] #[allow(non_camel_case_types, dead_code)]
}, },
@ -608,19 +570,19 @@ impl ToTokens for ParsedBundle {
generics: generics.into(), generics: generics.into(),
fields: Fields::Named(match_variant_fields), fields: Fields::Named(match_variant_fields),
semi_token: None, semi_token: None,
}; }
match_variant_struct.to_tokens(tokens); .to_tokens(tokens);
create_struct_debug_impl(&match_variant_struct, &struct_name, None).to_tokens(tokens);
let mut mask_type_sim_value_fields = mask_type_fields; let mut mask_type_sim_value_fields = mask_type_fields;
for Field { ty, .. } in &mut mask_type_sim_value_fields.named { for Field { ty, .. } in &mut mask_type_sim_value_fields.named {
*ty = parse_quote_spanned! {span=> *ty = parse_quote_spanned! {span=>
::fayalite::sim::value::SimValue<#ty> ::fayalite::sim::value::SimValue<#ty>
}; };
} }
let mask_type_sim_value_struct = ItemStruct { ItemStruct {
attrs: vec![ attrs: vec![
parse_quote_spanned! {span=> parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive( #[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone, ::fayalite::__std::clone::Clone,
)] )]
}, },
@ -634,34 +596,19 @@ impl ToTokens for ParsedBundle {
generics: generics.into(), generics: generics.into(),
fields: Fields::Named(mask_type_sim_value_fields), fields: Fields::Named(mask_type_sim_value_fields),
semi_token: None, semi_token: None,
};
mask_type_sim_value_struct.to_tokens(tokens);
if custom_debug_mask_sim.is_none() {
create_struct_debug_impl(
&mask_type_struct,
mask_type_name,
Some(CustomDebugTrait {
trait_path: &parse_quote_spanned! {span=>
::fayalite::ty::SimValueDebug
},
fn_name: &format_ident!("sim_value_debug", span = span),
this_arg: &parse_quote_spanned! {span=>
value: &<Self as ::fayalite::ty::Type>::SimValue
},
}),
)
.to_tokens(tokens);
} }
.to_tokens(tokens);
let mut sim_value_fields = FieldsNamed::from(fields.clone()); let mut sim_value_fields = FieldsNamed::from(fields.clone());
for Field { ty, .. } in &mut sim_value_fields.named { for Field { ty, .. } in &mut sim_value_fields.named {
*ty = parse_quote_spanned! {span=> *ty = parse_quote_spanned! {span=>
::fayalite::sim::value::SimValue<#ty> ::fayalite::sim::value::SimValue<#ty>
}; };
} }
let sim_value_struct = ItemStruct { ItemStruct {
attrs: vec![ attrs: vec![
parse_quote_spanned! {span=> parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive( #[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone, ::fayalite::__std::clone::Clone,
)] )]
}, },
@ -675,36 +622,8 @@ impl ToTokens for ParsedBundle {
generics: generics.into(), generics: generics.into(),
fields: Fields::Named(sim_value_fields), fields: Fields::Named(sim_value_fields),
semi_token: None, semi_token: None,
}; }
sim_value_struct.to_tokens(tokens);
if custom_debug_sim.is_none() {
create_struct_debug_impl(
&type_struct,
&struct_name,
Some(CustomDebugTrait {
trait_path: &parse_quote_spanned! {span=>
::fayalite::ty::SimValueDebug
},
fn_name: &format_ident!("sim_value_debug", span = span),
this_arg: &parse_quote_spanned! {span=>
value: &<Self as ::fayalite::ty::Type>::SimValue
},
}),
)
.to_tokens(tokens); .to_tokens(tokens);
}
if custom_sim_display.is_some() {
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics
#where_clause
{
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
<#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(self, f)
}
}
}.to_tokens(tokens);
}
let this_token = Ident::new("__this", span); let this_token = Ident::new("__this", span);
let fields_token = Ident::new("__fields", span); let fields_token = Ident::new("__fields", span);
let self_token = Token![self](span); let self_token = Token![self](span);
@ -775,10 +694,10 @@ impl ToTokens for ParsedBundle {
v.field(&value.#ident); v.field(&value.#ident);
} }
})); }));
let value_type_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap(); let ident: &Ident = field.ident().as_ref().unwrap();
quote_spanned! {span=> quote_spanned! {span=>
#ident: ::fayalite::expr::ValueType::ty(&self.#ident), #ident: ::fayalite::sim::value::SimValue::ty(&self.#ident),
} }
})); }));
let fields_len = fields.named().into_iter().len(); let fields_len = fields.named().into_iter().len();
@ -862,6 +781,7 @@ impl ToTokens for ParsedBundle {
#where_clause #where_clause
{ {
type Builder = #unfilled_mask_type_builder_ty; type Builder = #unfilled_mask_type_builder_ty;
type FilledBuilder = #filled_mask_type_builder_ty;
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
} }
@ -886,47 +806,28 @@ impl ToTokens for ParsedBundle {
} }
} }
#[automatically_derived] #[automatically_derived]
impl #impl_generics ::fayalite::__std::fmt::Debug for #mask_type_sim_value_ident #type_generics
#where_clause
{
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
<#mask_type_ident #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
}
}
#[automatically_derived]
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) -> <Self as ::fayalite::expr::ValueType>::Type {
#mask_type_ident {
#(#value_type_fields)*
}
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics
#where_clause #where_clause
{ {
type Type = #mask_type_ident #type_generics;
fn to_sim_value( fn to_sim_value(
&self, &self,
) -> ::fayalite::sim::value::SimValue< ) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::expr::ValueType>::Type, <Self as ::fayalite::sim::value::ToSimValue>::Type,
> { > {
let ty = #mask_type_ident { let ty = #mask_type_ident {
#(#value_type_fields)* #(#to_sim_value_fields)*
}; };
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
} }
fn into_sim_value( fn into_sim_value(
self, self,
) -> ::fayalite::sim::value::SimValue< ) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::expr::ValueType>::Type, <Self as ::fayalite::sim::value::ToSimValue>::Type,
> { > {
let ty = #mask_type_ident { let ty = #mask_type_ident {
#(#value_type_fields)* #(#to_sim_value_fields)*
}; };
::fayalite::sim::value::SimValue::from_value(ty, self) ::fayalite::sim::value::SimValue::from_value(ty, self)
} }
@ -1030,6 +931,7 @@ impl ToTokens for ParsedBundle {
#where_clause #where_clause
{ {
type Builder = #unfilled_builder_ty; type Builder = #unfilled_builder_ty;
type FilledBuilder = #filled_builder_ty;
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
} }
@ -1054,47 +956,28 @@ impl ToTokens for ParsedBundle {
} }
} }
#[automatically_derived] #[automatically_derived]
impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics
#where_clause
{
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
<#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
}
}
#[automatically_derived]
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) -> <Self as ::fayalite::expr::ValueType>::Type {
#target {
#(#value_type_fields)*
}
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
#where_clause #where_clause
{ {
type Type = #target #type_generics;
fn to_sim_value( fn to_sim_value(
&self, &self,
) -> ::fayalite::sim::value::SimValue< ) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::expr::ValueType>::Type, <Self as ::fayalite::sim::value::ToSimValue>::Type,
> { > {
let ty = #target { let ty = #target {
#(#value_type_fields)* #(#to_sim_value_fields)*
}; };
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
} }
fn into_sim_value( fn into_sim_value(
self, self,
) -> ::fayalite::sim::value::SimValue< ) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::expr::ValueType>::Type, <Self as ::fayalite::sim::value::ToSimValue>::Type,
> { > {
let ty = #target { let ty = #target {
#(#value_type_fields)* #(#to_sim_value_fields)*
}; };
::fayalite::sim::value::SimValue::from_value(ty, self) ::fayalite::sim::value::SimValue::from_value(ty, self)
} }
@ -1120,203 +1003,91 @@ impl ToTokens for ParsedBundle {
} }
.to_tokens(tokens); .to_tokens(tokens);
if let Some((cmp_eq,)) = cmp_eq { if let Some((cmp_eq,)) = cmp_eq {
let mut cmp_eq_where_clause = let mut expr_where_clause =
Generics::from(generics) Generics::from(generics)
.where_clause .where_clause
.unwrap_or_else(|| syn::WhereClause { .unwrap_or_else(|| syn::WhereClause {
where_token: Token![where](span), where_token: Token![where](span),
predicates: Punctuated::new(), predicates: Punctuated::new(),
}); });
let mut fields_value_eq = vec![]; let mut sim_value_where_clause = expr_where_clause.clone();
let mut fields_value_ne = vec![]; let mut fields_sim_value_eq = vec![];
let mut fields_expr_eq = vec![]; let mut fields_cmp_eq = vec![];
let mut fields_expr_ne = vec![]; let mut fields_cmp_ne = vec![];
let mut fields_valueless_eq = vec![];
let mut fields_valueless_ne = vec![];
let mut fields_structural_eq = vec![];
for field in fields.named() { for field in fields.named() {
let field_ident = field.ident(); let field_ident = field.ident();
let field_ty = field.ty(); let field_ty = field.ty();
cmp_eq_where_clause expr_where_clause
.predicates .predicates
.push(parse_quote_spanned! {cmp_eq.span=> .push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty> #field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
}); });
fields_structural_eq.push(quote_spanned! {cmp_eq.span=> sim_value_where_clause
<#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ .predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty>
}); });
fields_value_eq.push(quote_spanned! {span=> fields_sim_value_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_value_eq( ::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident)
__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_value_ne.push(quote_spanned! {span=> fields_cmp_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_value_ne( ::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
__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_expr_eq.push(quote_spanned! {span=> fields_cmp_ne.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq( ::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
__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 value_eq_body; let sim_value_eq_body;
let value_ne_body; let cmp_eq_body;
let expr_eq_body; let cmp_ne_body;
let expr_ne_body;
let valueless_eq_body;
let valueless_ne_body;
let structural_eq;
if fields_len == 0 { if fields_len == 0 {
value_eq_body = quote_spanned! {span=> sim_value_eq_body = quote_spanned! {span=>
true true
}; };
value_ne_body = quote_spanned! {span=> cmp_eq_body = quote_spanned! {span=>
false
};
expr_eq_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&true) ::fayalite::expr::ToExpr::to_expr(&true)
}; };
expr_ne_body = quote_spanned! {span=> cmp_ne_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&false) ::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)
};
structural_eq = quote_spanned! {span=>
true
};
} else { } else {
value_eq_body = quote_spanned! {span=> sim_value_eq_body = quote_spanned! {span=>
#(#fields_value_eq)&* #(#fields_sim_value_eq)&&*
}; };
value_ne_body = quote_spanned! {span=> cmp_eq_body = quote_spanned! {span=>
#(#fields_value_ne)|* #(#fields_cmp_eq)&*
}; };
expr_eq_body = quote_spanned! {span=> cmp_ne_body = quote_spanned! {span=>
#(#fields_expr_eq)&* #(#fields_cmp_ne)|*
};
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)|*
};
structural_eq = quote_spanned! {span=>
#(#fields_structural_eq)&&*
}; };
}; };
quote_spanned! {span=> quote_spanned! {span=>
#[automatically_derived] #[automatically_derived]
impl #impl_generics ::fayalite::expr::HdlPartialEqImpl<Self> for #target #type_generics impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
#cmp_eq_where_clause #expr_where_clause
{ {
const TRY_STRUCTURAL_EQ: ::fayalite::__std::primitive::bool = #structural_eq; fn cmp_eq(
#[track_caller]
fn cmp_value_eq(
__lhs: Self,
__lhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
__rhs: Self,
__rhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
) -> ::fayalite::__std::primitive::bool {
#value_eq_body
}
#[track_caller]
fn cmp_value_ne(
__lhs: Self,
__lhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
__rhs: Self,
__rhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
) -> ::fayalite::__std::primitive::bool {
#value_ne_body
}
#[track_caller]
fn cmp_expr_eq(
__lhs: ::fayalite::expr::Expr<Self>, __lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>, __rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::TRY_STRUCTURAL_EQ { #cmp_eq_body
if let ::fayalite::__std::result::Result::Ok(__retval) =
::fayalite::expr::ops::StructuralEq::try_new(
::fayalite::expr::Expr::canonical(__lhs),
::fayalite::expr::Expr::canonical(__rhs),
)
{
return ::fayalite::expr::ToExpr::to_expr(&__retval);
} }
} fn cmp_ne(
#expr_eq_body
}
#[track_caller]
fn cmp_expr_ne(
__lhs: ::fayalite::expr::Expr<Self>, __lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>, __rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::TRY_STRUCTURAL_EQ { #cmp_ne_body
return !::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::StructuralEq::new(
::fayalite::expr::Expr::canonical(__lhs),
::fayalite::expr::Expr::canonical(__rhs),
),
);
} }
#expr_ne_body
} }
#[automatically_derived]
#[track_caller] impl #impl_generics ::fayalite::sim::value::SimValuePartialEq<Self> for #target #type_generics
fn cmp_valueless_eq( #sim_value_where_clause
__lhs: ::fayalite::expr::Valueless<Self>, {
__rhs: ::fayalite::expr::Valueless<Self>, fn sim_value_eq(
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> { __lhs: &::fayalite::sim::value::SimValue<Self>,
#valueless_eq_body __rhs: &::fayalite::sim::value::SimValue<Self>,
} ) -> bool {
#sim_value_eq_body
#[track_caller]
fn cmp_valueless_ne(
__lhs: ::fayalite::expr::Valueless<Self>,
__rhs: ::fayalite::expr::Valueless<Self>,
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
#valueless_ne_body
} }
} }
} }

View file

@ -3,9 +3,8 @@
use crate::{ use crate::{
Errors, HdlAttr, PairsIterExt, Errors, HdlAttr, PairsIterExt,
hdl_type_common::{ hdl_type_common::{
CustomDebugOptions, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, SplitForImpl,
SplitForImpl, TypesParser, WrappedInConst, common_derives, create_struct_debug_impl, TypesParser, WrappedInConst, common_derives, get_target,
get_target,
}, },
kw, kw,
}; };
@ -159,31 +158,10 @@ impl ParsedEnum {
custom_bounds, custom_bounds,
no_static: _, no_static: _,
no_runtime_generics: _, no_runtime_generics: _,
cmp_eq: _, cmp_eq,
ref get,
custom_debug: _,
custom_sim_display: _,
} = options.body; } = options.body;
if let Some((get, ..)) = get { if let Some((cmp_eq,)) = cmp_eq {
errors.error(get, "#[hdl(get(...))] is not allowed on enums"); errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
}
let CustomDebugOptions {
type_: _,
sim: _,
mask_type,
mask_sim,
} = options.body.custom_debug();
if let Some((mask_type,)) = mask_type {
errors.error(
mask_type,
"#[hdl(custom_debug(mask_type)] is not allowed on enums",
);
}
if let Some((mask_sim,)) = mask_sim {
errors.error(
mask_sim,
"#[hdl(custom_debug(mask_sim)] is not allowed on enums",
);
} }
attrs.retain(|attr| { attrs.retain(|attr| {
if attr.path().is_ident("repr") { if attr.path().is_ident("repr") {
@ -246,21 +224,11 @@ impl ToTokens for ParsedEnum {
custom_bounds: _, custom_bounds: _,
no_static, no_static,
no_runtime_generics, no_runtime_generics,
cmp_eq, cmp_eq: _, // TODO: implement cmp_eq for enums
get: _,
custom_debug: _,
custom_sim_display,
} = &options.body; } = &options.body;
let CustomDebugOptions {
type_: custom_debug_type,
sim: custom_debug_sim,
mask_type: _,
mask_sim: _,
} = options.body.custom_debug();
let target = get_target(target, ident); let target = get_target(target, ident);
let enum_name = ident.to_string();
let mut struct_attrs = attrs.clone(); let mut struct_attrs = attrs.clone();
struct_attrs.push(common_derives(span, false)); struct_attrs.push(common_derives(span));
struct_attrs.push(parse_quote_spanned! {span=> struct_attrs.push(parse_quote_spanned! {span=>
#[allow(non_snake_case)] #[allow(non_snake_case)]
}); });
@ -300,7 +268,7 @@ impl ToTokens for ParsedEnum {
} }
}, },
)); ));
let type_struct = ItemStruct { ItemStruct {
attrs: struct_attrs, attrs: struct_attrs,
vis: vis.clone(), vis: vis.clone(),
struct_token: Token![struct](enum_token.span), struct_token: Token![struct](enum_token.span),
@ -315,8 +283,8 @@ impl ToTokens for ParsedEnum {
}) })
}, },
semi_token: None, semi_token: None,
}; }
type_struct.to_tokens(tokens); .to_tokens(tokens);
let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
if let (MaybeParsed::Parsed(generics), None) = (generics, no_runtime_generics) { if let (MaybeParsed::Parsed(generics), None) = (generics, no_runtime_generics) {
generics.make_runtime_generics(tokens, vis, ident, &target, |context| { generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
@ -400,9 +368,6 @@ impl ToTokens for ParsedEnum {
} }
.to_tokens(tokens); .to_tokens(tokens);
} }
if custom_debug_type.is_none() {
create_struct_debug_impl(&type_struct, &enum_name, None).to_tokens(tokens);
}
let mut enum_attrs = attrs.clone(); let mut enum_attrs = attrs.clone();
enum_attrs.push(parse_quote_spanned! {span=> enum_attrs.push(parse_quote_spanned! {span=>
#[allow(dead_code, non_camel_case_types)] #[allow(dead_code, non_camel_case_types)]
@ -483,6 +448,7 @@ impl ToTokens for ParsedEnum {
let mut enum_attrs = attrs.clone(); let mut enum_attrs = attrs.clone();
enum_attrs.push(parse_quote_spanned! {span=> enum_attrs.push(parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive( #[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone, ::fayalite::__std::clone::Clone,
)] )]
}); });
@ -583,6 +549,7 @@ impl ToTokens for ParsedEnum {
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
if let Some(ParsedVariantField { ty, .. }) = field { if let Some(ParsedVariantField { ty, .. }) = field {
quote_spanned! {span=> quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics #target #type_generics impl #impl_generics #target #type_generics
#where_clause #where_clause
{ {
@ -604,6 +571,7 @@ impl ToTokens for ParsedEnum {
) )
} }
} }
#[automatically_derived]
impl #impl_generics #sim_builder_ident #type_generics impl #impl_generics #sim_builder_ident #type_generics
#where_clause #where_clause
{ {
@ -625,6 +593,7 @@ impl ToTokens for ParsedEnum {
} }
} else { } else {
quote_spanned! {span=> quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics #target #type_generics impl #impl_generics #target #type_generics
#where_clause #where_clause
{ {
@ -639,13 +608,12 @@ impl ToTokens for ParsedEnum {
) )
} }
} }
#[automatically_derived]
impl #impl_generics #sim_builder_ident #type_generics impl #impl_generics #sim_builder_ident #type_generics
#where_clause #where_clause
{ {
#[allow(non_snake_case, dead_code)] #[allow(non_snake_case, dead_code)]
#vis fn #ident( #vis fn #ident(#self_token) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
#self_token,
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
::fayalite::sim::value::SimValue::from_value( ::fayalite::sim::value::SimValue::from_value(
#self_token.#sim_builder_ty_field_ident, #self_token.#sim_builder_ty_field_ident,
#sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()), #sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()),
@ -869,290 +837,6 @@ impl ToTokens for ParsedEnum {
}, },
)), )),
); );
if custom_debug_sim.is_none() {
let debug_match_arms = Vec::from_iter(
variants
.iter()
.map(
|ParsedVariant {
attrs: _,
options: _,
ident,
field,
}| {
let variant_name = ident.to_string();
if let Some(_) = field {
quote_spanned! {span=>
#sim_value_ident::#ident(field, _) => {
f.debug_tuple(#variant_name).field(field).finish()
}
}
} else {
quote_spanned! {span=>
#sim_value_ident::#ident(_) => {
f.write_str(#variant_name)
}
}
}
},
)
.chain(sim_value_unknown_variant_name.as_ref().map(
|sim_value_unknown_variant_name| {
let sim_value_unknown_variant_name_str =
sim_value_unknown_variant_name.to_string();
quote_spanned! {span=>
#sim_value_ident::#sim_value_unknown_variant_name(_) => {
f.write_str(#sim_value_unknown_variant_name_str)
}
}
},
)),
);
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::ty::SimValueDebug for #target #type_generics
#where_clause
{
fn sim_value_debug(
value: &<Self as ::fayalite::ty::Type>::SimValue,
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
) -> ::fayalite::__std::fmt::Result {
match value {
#(#debug_match_arms)*
}
}
}
}
.to_tokens(tokens);
}
if custom_sim_display.is_some() {
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics
#where_clause
{
fn fmt(
&self,
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
) -> ::fayalite::__std::fmt::Result {
<#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(
self,
f,
)
}
}
}.to_tokens(tokens);
}
if let Some((cmp_eq,)) = cmp_eq {
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 variants_value_eq = vec![];
let mut variants_expr_eq = vec![];
let mut fields_valueless_eq = vec![];
let mut structural_eq: Option<TokenStream> = None;
for (
variant_index,
ParsedVariant {
attrs: _,
options: variant_options,
ident: variant_ident,
field,
},
) in variants.iter().enumerate()
{
let VariantOptions {} = variant_options.body;
if let Some(ParsedVariantField {
paren_token: _,
attrs: _,
options: field_options,
ty: field_ty,
comma_token: _,
}) = field
{
let FieldOptions {} = field_options.body;
cmp_eq_where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty>
});
match &mut structural_eq {
Some(structural_eq) => {
structural_eq.extend(quote_spanned! {cmp_eq.span=>
&& <#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ
});
}
None => {
structural_eq = Some(quote_spanned! {cmp_eq.span=>
<#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ
});
}
}
variants_value_eq.push(quote_spanned! {span=>
(
#sim_value_ident::#variant_ident(__lhs_field, _),
#sim_value_ident::#variant_ident(__rhs_field, _),
) => {
::fayalite::expr::HdlPartialEqImpl::cmp_value_eq(
__lhs.#variant_ident,
::fayalite::__std::borrow::Cow::Borrowed(__lhs_field),
__rhs.#variant_ident,
::fayalite::__std::borrow::Cow::Borrowed(__rhs_field),
)
}
});
variants_expr_eq.push(quote_spanned! {span=>
{
let (#match_variant_ident::#variant_ident(__lhs), __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
::fayalite::__std::iter::Iterator::next(&mut __lhs_match_variant_iter)
.expect("known to have enough variants"),
)
else {
::fayalite::__std::unreachable!();
};
let (#match_variant_ident::#variant_ident(__rhs), __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
::fayalite::__std::iter::Iterator::nth(
&mut ::fayalite::module::match_(__rhs),
#variant_index,
)
.expect("known to have variant"),
)
else {
::fayalite::__std::unreachable!();
};
::fayalite::module::connect(
__retval,
::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(__lhs, __rhs),
);
}
});
fields_valueless_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq(
::fayalite::expr::Valueless::new(__lhs.#variant_ident),
::fayalite::expr::Valueless::new(__rhs.#variant_ident),
)
});
} else {
variants_value_eq.push(quote_spanned! {span=>
(#sim_value_ident::#variant_ident(_), #sim_value_ident::#variant_ident(_)) => true,
});
variants_expr_eq.push(quote_spanned! {span=>
{
let (#match_variant_ident::#variant_ident, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
::fayalite::__std::iter::Iterator::next(&mut __lhs_match_variant_iter)
.expect("known to have enough variants"),
)
else {
::fayalite::__std::unreachable!();
};
let (#match_variant_ident::#variant_ident, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
::fayalite::__std::iter::Iterator::nth(
&mut ::fayalite::module::match_(__rhs),
#variant_index,
)
.expect("known to have variant"),
)
else {
::fayalite::__std::unreachable!();
};
::fayalite::module::connect(__retval, true);
}
});
}
}
if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name {
variants_value_eq.push(quote_spanned! {span=>
(
#sim_value_ident::#sim_value_unknown_variant_name(__lhs_unknown),
#sim_value_ident::#sim_value_unknown_variant_name(__rhs_unknown),
) => {
__lhs_unknown == __rhs_unknown
}
});
}
let valueless_eq_body = if fields_valueless_eq.is_empty() {
quote_spanned! {span=>
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
}
} else {
quote_spanned! {span=>
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
#(#fields_valueless_eq)&*
}
};
let cmp_expr_eq_wire_name = format!("{ident}_cmp_eq");
let structural_eq = structural_eq.unwrap_or_else(|| {
quote_spanned! {span=>
true
}
});
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::expr::HdlPartialEqImpl<Self> for #target #type_generics
#cmp_eq_where_clause
{
const TRY_STRUCTURAL_EQ: ::fayalite::__std::primitive::bool = #structural_eq;
#[track_caller]
fn cmp_value_eq(
__lhs: Self,
__lhs_value: ::fayalite::__std::borrow::Cow<'_,
<Self as ::fayalite::ty::Type>::SimValue>,
__rhs: Self,
__rhs_value: ::fayalite::__std::borrow::Cow<'_,
<Self as ::fayalite::ty::Type>::SimValue>,
) -> ::fayalite::__std::primitive::bool {
match (&*__lhs_value, &*__rhs_value) {
#(#variants_value_eq)*
_ => false,
}
}
#[track_caller]
fn cmp_expr_eq(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::TRY_STRUCTURAL_EQ {
if let ::fayalite::__std::result::Result::Ok(__retval) =
::fayalite::expr::ops::StructuralEq::try_new(
::fayalite::expr::Expr::canonical(__lhs),
::fayalite::expr::Expr::canonical(__rhs),
)
{
return ::fayalite::expr::ToExpr::to_expr(&__retval);
}
}
let __retval = ::fayalite::module::wire(
::fayalite::module::ImplicitName(#cmp_expr_eq_wire_name),
::fayalite::int::Bool,
);
::fayalite::module::connect(__retval, false);
let mut __lhs_match_variant_iter = ::fayalite::module::match_(__lhs);
#(#variants_expr_eq)*
__retval
}
#[track_caller]
fn cmp_valueless_eq(
__lhs: ::fayalite::expr::Valueless<Self>,
__rhs: ::fayalite::expr::Valueless<Self>,
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
#valueless_eq_body
}
}
}
.to_tokens(tokens);
}
let variants_len = variants.len(); let variants_len = variants.len();
quote_spanned! {span=> quote_spanned! {span=>
#[automatically_derived] #[automatically_derived]
@ -1164,8 +848,7 @@ impl ToTokens for ParsedEnum {
type SimValue = #sim_value_ident #type_generics; type SimValue = #sim_value_ident #type_generics;
type MatchVariant = #match_variant_ident #type_generics; type MatchVariant = #match_variant_ident #type_generics;
type MatchActiveScope = ::fayalite::module::Scope; type MatchActiveScope = ::fayalite::module::Scope;
type MatchVariantAndInactiveScope = type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>; type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
fn match_variants( fn match_variants(
@ -1178,9 +861,7 @@ impl ToTokens for ParsedEnum {
::fayalite::int::Bool ::fayalite::int::Bool
} }
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType { fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType {
::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new( ::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(#self_token)))
::fayalite::enum_::EnumType::variants(#self_token),
))
} }
#[track_caller] #[track_caller]
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -1189,11 +870,7 @@ impl ToTokens for ParsedEnum {
::fayalite::__std::panic!("expected enum"); ::fayalite::__std::panic!("expected enum");
}; };
let #variants_token = ::fayalite::enum_::EnumType::variants(&enum_); let #variants_token = ::fayalite::enum_::EnumType::variants(&enum_);
::fayalite::__std::assert_eq!( ::fayalite::__std::assert_eq!(#variants_token.len(), #variants_len, "enum has wrong number of variants");
#variants_token.len(),
#variants_len,
"enum has wrong number of variants",
);
Self { Self {
#(#from_canonical_body_fields)* #(#from_canonical_body_fields)*
} }
@ -1210,7 +887,6 @@ impl ToTokens for ParsedEnum {
#(#sim_value_from_opaque_match_arms)* #(#sim_value_from_opaque_match_arms)*
} }
} }
#[allow(irrefutable_let_patterns)]
fn sim_value_clone_from_opaque( fn sim_value_clone_from_opaque(
&self, &self,
value: &mut <Self as ::fayalite::ty::Type>::SimValue, value: &mut <Self as ::fayalite::ty::Type>::SimValue,
@ -1239,10 +915,7 @@ impl ToTokens for ParsedEnum {
type SimBuilder = #sim_builder_ident #type_generics; type SimBuilder = #sim_builder_ident #type_generics;
fn match_activate_scope( fn match_activate_scope(
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
) -> ( ) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) {
<Self as ::fayalite::ty::Type>::MatchVariant,
<Self as ::fayalite::ty::Type>::MatchActiveScope,
) {
let (#variant_access_token, scope) = v.activate(); let (#variant_access_token, scope) = v.activate();
( (
match #variant_access_token.variant_index() { match #variant_access_token.variant_index() {
@ -1259,17 +932,6 @@ impl ToTokens for ParsedEnum {
} }
} }
#[automatically_derived] #[automatically_derived]
impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics
#where_clause
{
fn fmt(
&self,
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
) -> ::fayalite::__std::fmt::Result {
<#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics> impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics>
for #sim_value_ident #type_generics for #sim_value_ident #type_generics
#where_clause #where_clause
@ -1278,10 +940,7 @@ impl ToTokens for ParsedEnum {
&self, &self,
ty: #target #type_generics, ty: #target #type_generics,
) -> ::fayalite::sim::value::SimValue<#target #type_generics> { ) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
::fayalite::sim::value::SimValue::from_value( ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
ty,
::fayalite::__std::clone::Clone::clone(self),
)
} }
fn into_sim_value_with_type( fn into_sim_value_with_type(
self, self,
@ -1364,26 +1023,16 @@ impl ToTokens for ParsedEnum {
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
} }
#[automatically_derived] #[automatically_derived]
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) -> <Self as ::fayalite::expr::ValueType>::Type {
::fayalite::ty::StaticType::TYPE
}
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::sim::value::ToSimValue impl #static_impl_generics ::fayalite::sim::value::ToSimValue
for #sim_value_ident #static_type_generics for #sim_value_ident #static_type_generics
#static_where_clause #static_where_clause
{ {
type Type = #target #static_type_generics;
fn to_sim_value( fn to_sim_value(
&self, &self,
) -> ::fayalite::sim::value::SimValue< ) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::expr::ValueType>::Type, <Self as ::fayalite::sim::value::ToSimValue>::Type,
> { > {
::fayalite::sim::value::SimValue::from_value( ::fayalite::sim::value::SimValue::from_value(
::fayalite::ty::StaticType::TYPE, ::fayalite::ty::StaticType::TYPE,
@ -1393,7 +1042,7 @@ impl ToTokens for ParsedEnum {
fn into_sim_value( fn into_sim_value(
self, self,
) -> ::fayalite::sim::value::SimValue< ) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::expr::ValueType>::Type, <Self as ::fayalite::sim::value::ToSimValue>::Type,
> { > {
::fayalite::sim::value::SimValue::from_value( ::fayalite::sim::value::SimValue::from_value(
::fayalite::ty::StaticType::TYPE, ::fayalite::ty::StaticType::TYPE,

View file

@ -3,275 +3,29 @@
use crate::{ use crate::{
Errors, HdlAttr, Errors, HdlAttr,
hdl_type_common::{ hdl_type_common::{
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, TypesParser,
PhantomConstGetBound, TypesParser, WrappedInConst, common_derives, get_target, known_items, get_target,
}, },
kw, kw,
}; };
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{ToTokens, format_ident, quote_spanned}; use quote::ToTokens;
use syn::{ use syn::{Attribute, Generics, Ident, ItemType, Token, Type, Visibility, parse_quote_spanned};
Attribute, Expr, Fields, GenericParam, Generics, Ident, ItemStruct, ItemType, Token, Type,
TypeGroup, TypeParam, TypeParen, Visibility, parse_quote_spanned, punctuated::Pair,
token::Paren,
};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct PhantomConstAccessorTypeParam { pub(crate) struct ParsedTypeAlias {
attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
ident: Ident, pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
colon_token: Token![:], pub(crate) vis: Visibility,
phantom_const_get_bound: PhantomConstGetBound, pub(crate) type_token: Token![type],
plus_token: Option<Token![+]>, pub(crate) ident: Ident,
} pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
pub(crate) eq_token: Token![=],
impl From<PhantomConstAccessorTypeParam> for TypeParam { pub(crate) ty: MaybeParsed<ParsedType, Type>,
fn from(value: PhantomConstAccessorTypeParam) -> Self { pub(crate) semi_token: Token![;],
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<PhantomConstAccessorTypeParam> for GenericParam {
fn from(value: PhantomConstAccessorTypeParam) -> Self {
TypeParam::from(value).into()
}
}
impl PhantomConstAccessorTypeParam {
fn parse_opt(generic_param: GenericParam) -> Option<Self> {
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<Token![,]>,
gt_token: Token![>],
}
impl From<PhantomConstAccessorGenerics> 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<Self> {
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<Attribute>,
options: HdlAttr<ItemOptions, kw::hdl>,
vis: Visibility,
type_token: Token![type],
ident: Ident,
generics: MaybeParsed<ParsedGenerics, Generics>,
eq_token: Token![=],
ty: MaybeParsed<ParsedType, Type>,
semi_token: Token![;],
},
PhantomConstAccessor {
attrs: Vec<Attribute>,
options: HdlAttr<ItemOptions, kw::hdl>,
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<known_items::DynSize>,
semi_token: Token![;],
},
} }
impl ParsedTypeAlias { impl ParsedTypeAlias {
fn ty_is_dyn_size(ty: &Type) -> Option<known_items::DynSize> {
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<ItemOptions, kw::hdl>,
get: (kw::get, Paren, Expr),
) -> syn::Result<Self> {
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: _,
ref custom_debug,
custom_sim_display,
} = 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_debug, _, _)) = custom_debug {
errors.error(custom_debug, "custom_debug is not valid on type aliases");
}
if let Some((custom_sim_display,)) = custom_sim_display {
errors.error(
custom_sim_display,
"custom_sim_display 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<P: PhantomConstGet<MyType>> = 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<Self> { fn parse(item: ItemType) -> syn::Result<Self> {
let ItemType { let ItemType {
mut attrs, mut attrs,
@ -297,42 +51,13 @@ impl ParsedTypeAlias {
no_static, no_static,
no_runtime_generics: _, no_runtime_generics: _,
cmp_eq, cmp_eq,
ref mut get,
ref custom_debug,
custom_sim_display,
} = options.body; } = 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 { if let Some((no_static,)) = no_static {
errors.error(no_static, "no_static is not valid on type aliases"); errors.error(no_static, "no_static is not valid on type aliases");
} }
if let Some((cmp_eq,)) = cmp_eq { if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
} }
if let Some((custom_debug, _, _)) = custom_debug {
errors.error(custom_debug, "custom_debug is not valid on type aliases");
}
if let Some((custom_sim_display,)) = custom_sim_display {
errors.error(
custom_sim_display,
"custom_sim_display is not valid on type aliases",
);
}
let generics = if custom_bounds.is_some() { let generics = if custom_bounds.is_some() {
MaybeParsed::Unrecognized(generics) MaybeParsed::Unrecognized(generics)
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
@ -342,7 +67,7 @@ impl ParsedTypeAlias {
}; };
let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors); let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors);
errors.finish()?; errors.finish()?;
Ok(Self::TypeAlias { Ok(Self {
attrs, attrs,
options, options,
vis, vis,
@ -358,8 +83,7 @@ impl ParsedTypeAlias {
impl ToTokens for ParsedTypeAlias { impl ToTokens for ParsedTypeAlias {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
match self { let Self {
Self::TypeAlias {
attrs, attrs,
options, options,
vis, vis,
@ -369,7 +93,7 @@ impl ToTokens for ParsedTypeAlias {
eq_token, eq_token,
ty, ty,
semi_token, semi_token,
} => { } = self;
let ItemOptions { let ItemOptions {
outline_generated: _, outline_generated: _,
target, target,
@ -377,9 +101,6 @@ impl ToTokens for ParsedTypeAlias {
no_static: _, no_static: _,
no_runtime_generics, no_runtime_generics,
cmp_eq: _, cmp_eq: _,
get: _,
custom_debug: _,
custom_sim_display: _,
} = &options.body; } = &options.body;
let target = get_target(target, ident); let target = get_target(target, ident);
let mut type_attrs = attrs.clone(); let mut type_attrs = attrs.clone();
@ -405,112 +126,11 @@ impl ToTokens for ParsedTypeAlias {
}) })
} }
} }
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: _,
custom_debug: _,
custom_sim_display: _,
} = &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, true),
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<TokenStream> { pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result<TokenStream> {
let item = ParsedTypeAlias::parse(item)?; let item = ParsedTypeAlias::parse(item)?;
let outline_generated = match &item { let outline_generated = item.options.body.outline_generated;
ParsedTypeAlias::TypeAlias { options, .. }
| ParsedTypeAlias::PhantomConstAccessor { options, .. } => options.body.outline_generated,
};
let mut contents = item.to_token_stream(); let mut contents = item.to_token_stream();
if outline_generated.is_some() { if outline_generated.is_some() {
contents = crate::outline_generated(contents, "hdl-type-alias-"); contents = crate::outline_generated(contents, "hdl-type-alias-");

View file

@ -7,10 +7,10 @@ use std::{collections::HashMap, fmt, mem};
use syn::{ use syn::{
AngleBracketedGenericArguments, Attribute, Block, ConstParam, Expr, ExprBlock, ExprGroup, AngleBracketedGenericArguments, Attribute, Block, ConstParam, Expr, ExprBlock, ExprGroup,
ExprIndex, ExprParen, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed, ExprIndex, ExprParen, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed,
FieldsUnnamed, FnArg, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, FieldsUnnamed, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, ItemStruct,
ItemStruct, Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, TraitBound, Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, Turbofish, Type,
Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, TypeParamBound, TypeParen, TypePath, TypeGenerics, TypeGroup, TypeParam, TypeParen, TypePath, TypeTuple, Visibility, WhereClause,
TypeTuple, Visibility, WhereClause, WherePredicate, WherePredicate,
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
parse_quote, parse_quote_spanned, parse_quote, parse_quote_spanned,
punctuated::{Pair, Punctuated}, punctuated::{Pair, Punctuated},
@ -18,17 +18,6 @@ use syn::{
token::{Brace, Bracket, Paren}, token::{Brace, Bracket, Paren},
}; };
crate::options! {
#[options = CustomDebugOptions]
#[no_ident_fragment]
pub(crate) enum CustomDebugOption {
Type(type_),
Sim(sim),
MaskType(mask_type),
MaskSim(mask_sim),
}
}
crate::options! { crate::options! {
#[options = ItemOptions] #[options = ItemOptions]
pub(crate) enum ItemOption { pub(crate) enum ItemOption {
@ -38,9 +27,6 @@ crate::options! {
NoStatic(no_static), NoStatic(no_static),
NoRuntimeGenerics(no_runtime_generics), NoRuntimeGenerics(no_runtime_generics),
CmpEq(cmp_eq), CmpEq(cmp_eq),
Get(get, Expr),
CustomDebug(custom_debug, CustomDebugOptions),
CustomSimDisplay(custom_sim_display),
} }
} }
@ -54,36 +40,8 @@ impl ItemOptions {
{ {
self.no_static = Some((kw::no_static(custom_bounds.span),)); self.no_static = Some((kw::no_static(custom_bounds.span),));
} }
if let Some((kw, _, custom_debug)) = &mut self.custom_debug {
if let CustomDebugOptions {
type_: None,
sim: None,
mask_type: None,
mask_sim: None,
} = custom_debug
{
*custom_debug = CustomDebugOptions {
type_: Some((kw::type_(kw.span),)),
sim: Some((kw::sim(kw.span),)),
mask_type: None,
mask_sim: None,
};
}
}
Ok(()) Ok(())
} }
pub(crate) fn custom_debug(&self) -> &CustomDebugOptions {
self.custom_debug.as_ref().map(|v| &v.2).unwrap_or(
const {
&CustomDebugOptions {
type_: None,
sim: None,
mask_type: None,
mask_sim: None,
}
},
)
}
} }
pub(crate) struct WrappedInConst<'a> { pub(crate) struct WrappedInConst<'a> {
@ -125,17 +83,10 @@ pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident:
} }
} }
pub(crate) fn common_derives(span: Span, include_debug: bool) -> Attribute { pub(crate) fn common_derives(span: Span) -> Attribute {
let debug = include_debug
.then(|| {
quote_spanned! {span=>
::fayalite::__std::fmt::Debug
}
})
.into_iter();
parse_quote_spanned! {span=> parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive( #[::fayalite::__std::prelude::v1::derive(
#(#debug,)* ::fayalite::__std::fmt::Debug,
::fayalite::__std::cmp::Eq, ::fayalite::__std::cmp::Eq,
::fayalite::__std::cmp::PartialEq, ::fayalite::__std::cmp::PartialEq,
::fayalite::__std::hash::Hash, ::fayalite::__std::hash::Hash,
@ -2094,7 +2045,6 @@ pub(crate) mod known_items {
impl_known_item!(::fayalite::int::Size); impl_known_item!(::fayalite::int::Size);
impl_known_item!(::fayalite::int::UInt); impl_known_item!(::fayalite::int::UInt);
impl_known_item!(::fayalite::int::UIntType); impl_known_item!(::fayalite::int::UIntType);
impl_known_item!(::fayalite::phantom_const::PhantomConstGet);
impl_known_item!(::fayalite::reset::ResetType); impl_known_item!(::fayalite::reset::ResetType);
impl_known_item!(::fayalite::ty::CanonicalType); impl_known_item!(::fayalite::ty::CanonicalType);
impl_known_item!(::fayalite::ty::StaticType); impl_known_item!(::fayalite::ty::StaticType);
@ -2113,174 +2063,6 @@ 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<Token![::]>,
pub(crate) lt_token: Token![<],
pub(crate) ty: Type,
pub(crate) comma_token: Option<Token![,]>,
pub(crate) gt_token: Token![>],
}
impl PhantomConstGetBound {
pub(crate) fn parse_path_with_arguments(path: Path) -> syn::Result<Result<Self, Path>> {
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<Self> {
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<GenericArgument, Token![,]>, 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<Result<Self, TypeParamBound>> {
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<PhantomConstGetBound> 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<PhantomConstGetBound> 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<PhantomConstGetBound> for TypeParamBound {
fn from(value: PhantomConstGetBound) -> Self {
TraitBound::from(value).into()
}
}
macro_rules! impl_bounds { macro_rules! impl_bounds {
( (
#[struct = $struct_type:ident] #[struct = $struct_type:ident]
@ -2288,10 +2070,6 @@ macro_rules! impl_bounds {
$( $(
$Variant:ident, $Variant:ident,
)* )*
$(
#[has_body]
$VariantHasBody:ident($variant_has_body_ty:ty),
)*
$( $(
#[unknown] #[unknown]
$Unknown:ident, $Unknown:ident,
@ -2301,7 +2079,6 @@ macro_rules! impl_bounds {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
$vis enum $enum_type { $vis enum $enum_type {
$($Variant(known_items::$Variant),)* $($Variant(known_items::$Variant),)*
$($VariantHasBody($variant_has_body_ty),)*
$($Unknown(syn::TypeParamBound),)? $($Unknown(syn::TypeParamBound),)?
} }
@ -2311,42 +2088,31 @@ 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 { impl ToTokens for $enum_type {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
match self { match self {
$(Self::$Variant(v) => v.to_tokens(tokens),)* $(Self::$Variant(v) => v.to_tokens(tokens),)*
$(Self::$VariantHasBody(v) => v.to_tokens(tokens),)*
$(Self::$Unknown(v) => v.to_tokens(tokens),)? $(Self::$Unknown(v) => v.to_tokens(tokens),)?
} }
} }
} }
impl $enum_type { impl $enum_type {
$vis fn parse_path_with_arguments(path: Path) -> syn::Result<Result<Self, Path>> { $vis fn parse_path(path: Path) -> Result<Self, Path> {
#![allow(unreachable_code)] #![allow(unreachable_code)]
$(let path = match known_items::$Variant::parse_path(path) { $(let path = match known_items::$Variant::parse_path(path) {
Ok(v) => return Ok(Ok(Self::$Variant(v))), Ok(v) => return Ok(Self::$Variant(v)),
Err(path) => path, Err(path) => path,
};)* };)*
$(let path = match <$variant_has_body_ty>::parse_path_with_arguments(path)? { $(return Ok(Self::$Unknown(syn::TraitBound {
Ok(v) => return Ok(Ok(Self::$VariantHasBody(v))),
Err(path) => path,
};)*
$(return Ok(Ok(Self::$Unknown(syn::TraitBound {
paren_token: None, paren_token: None,
modifier: syn::TraitBoundModifier::None, modifier: syn::TraitBoundModifier::None,
lifetimes: None, lifetimes: None,
path, path,
}.into())));)? }.into()));)?
Ok(Err(path)) Err(path)
} }
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> syn::Result<Result<Self, syn::TypeParamBound>> { $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> {
#![allow(unreachable_code)] #![allow(unreachable_code)]
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound { if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
if let syn::TraitBound { if let syn::TraitBound {
@ -2355,24 +2121,24 @@ macro_rules! impl_bounds {
lifetimes: None, lifetimes: None,
path: _, path: _,
} = trait_bound { } = trait_bound {
match Self::parse_path_with_arguments(trait_bound.path)? { match Self::parse_path(trait_bound.path) {
Ok(retval) => return Ok(Ok(retval)), Ok(retval) => return Ok(retval),
Err(path) => trait_bound.path = path, Err(path) => trait_bound.path = path,
} }
} }
type_param_bound = trait_bound.into(); type_param_bound = trait_bound.into();
} }
$(return Ok(Ok(Self::$Unknown(type_param_bound)));)? $(return Ok(Self::$Unknown(type_param_bound));)?
Ok(Err(type_param_bound)) Err(type_param_bound)
} }
} }
impl Parse for $enum_type { impl Parse for $enum_type {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
Self::parse_type_param_bound(input.parse()?)? Self::parse_type_param_bound(input.parse()?)
.map_err(|type_param_bound| syn::Error::new_spanned( .map_err(|type_param_bound| syn::Error::new_spanned(
type_param_bound, type_param_bound,
format_args!("expected one of: {}", [$(stringify!($Variant),)* $(stringify!($VariantHasBody)),*].join(", ")), format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
)) ))
} }
} }
@ -2381,7 +2147,6 @@ macro_rules! impl_bounds {
#[allow(non_snake_case)] #[allow(non_snake_case)]
$vis struct $struct_type { $vis struct $struct_type {
$($vis $Variant: Option<known_items::$Variant>,)* $($vis $Variant: Option<known_items::$Variant>,)*
$($vis $VariantHasBody: Option<$variant_has_body_ty>,)*
$($vis $Unknown: Vec<syn::TypeParamBound>,)? $($vis $Unknown: Vec<syn::TypeParamBound>,)?
} }
@ -2394,11 +2159,6 @@ macro_rules! impl_bounds {
separator = Some(<Token![+]>::default()); separator = Some(<Token![+]>::default());
v.to_tokens(tokens); v.to_tokens(tokens);
})* })*
$(if let Some(v) = &self.$VariantHasBody {
separator.to_tokens(tokens);
separator = Some(<Token![+]>::default());
v.to_tokens(tokens);
})*
$(for v in &self.$Unknown { $(for v in &self.$Unknown {
separator.to_tokens(tokens); separator.to_tokens(tokens);
separator = Some(<Token![+]>::default()); separator = Some(<Token![+]>::default());
@ -2412,7 +2172,6 @@ macro_rules! impl_bounds {
#[allow(non_snake_case)] #[allow(non_snake_case)]
$vis struct Iter { $vis struct Iter {
$($Variant: Option<known_items::$Variant>,)* $($Variant: Option<known_items::$Variant>,)*
$($VariantHasBody: Option<$variant_has_body_ty>,)*
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)? $($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
} }
@ -2423,7 +2182,6 @@ macro_rules! impl_bounds {
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
Iter { Iter {
$($Variant: self.$Variant,)* $($Variant: self.$Variant,)*
$($VariantHasBody: self.$VariantHasBody,)*
$($Unknown: self.$Unknown.into_iter(),)? $($Unknown: self.$Unknown.into_iter(),)?
} }
} }
@ -2438,11 +2196,6 @@ macro_rules! impl_bounds {
return Some($enum_type::$Variant(value)); 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() { if let Some(value) = self.$Unknown.next() {
return Some($enum_type::$Unknown(value)); return Some($enum_type::$Unknown(value));
@ -2458,11 +2211,6 @@ macro_rules! impl_bounds {
init = f(init, $enum_type::$Variant(value)); 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() { if let Some(value) = self.$Unknown.next() {
init = f(init, $enum_type::$Unknown(value)); init = f(init, $enum_type::$Unknown(value));
@ -2479,9 +2227,6 @@ macro_rules! impl_bounds {
$($enum_type::$Variant(v) => { $($enum_type::$Variant(v) => {
self.$Variant = Some(v); self.$Variant = Some(v);
})* })*
$($enum_type::$VariantHasBody(v) => {
self.$VariantHasBody = Some(v);
})*
$($enum_type::$Unknown(v) => { $($enum_type::$Unknown(v) => {
self.$Unknown.push(v); self.$Unknown.push(v);
})? })?
@ -2503,9 +2248,6 @@ macro_rules! impl_bounds {
$(if let Some(v) = v.$Variant { $(if let Some(v) = v.$Variant {
self.$Variant = Some(v); self.$Variant = Some(v);
})* })*
$(if let Some(v) = v.$VariantHasBody {
self.$VariantHasBody = Some(v);
})*
$(self.$Unknown.extend(v.$Unknown);)* $(self.$Unknown.extend(v.$Unknown);)*
}); });
} }
@ -2560,8 +2302,6 @@ impl_bounds! {
Size, Size,
StaticType, StaticType,
Type, Type,
#[has_body]
PhantomConstGet(PhantomConstGetBound),
#[unknown] #[unknown]
Unknown, Unknown,
} }
@ -2577,8 +2317,6 @@ impl_bounds! {
ResetType, ResetType,
StaticType, StaticType,
Type, Type,
#[has_body]
PhantomConstGet(PhantomConstGetBound),
#[unknown] #[unknown]
Unknown, Unknown,
} }
@ -2594,7 +2332,6 @@ impl From<ParsedTypeBound> for ParsedBound {
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
ParsedTypeBound::Type(v) => ParsedBound::Type(v), ParsedTypeBound::Type(v) => ParsedBound::Type(v),
ParsedTypeBound::PhantomConstGet(v) => ParsedBound::PhantomConstGet(v),
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
} }
} }
@ -2610,7 +2347,6 @@ impl From<ParsedTypeBounds> for ParsedBounds {
ResetType, ResetType,
StaticType, StaticType,
Type, Type,
PhantomConstGet,
Unknown, Unknown,
} = value; } = value;
Self { Self {
@ -2623,7 +2359,6 @@ impl From<ParsedTypeBounds> for ParsedBounds {
Size: None, Size: None,
StaticType, StaticType,
Type, Type,
PhantomConstGet,
Unknown, Unknown,
} }
} }
@ -2660,10 +2395,6 @@ impl ParsedTypeBound {
ParsedTypeBound::Type(known_items::Type(span)), ParsedTypeBound::Type(known_items::Type(span)),
]), ]),
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
Self::PhantomConstGet(v) => ParsedTypeBounds::from_iter([
ParsedTypeBound::from(v),
ParsedTypeBound::Type(known_items::Type(span)),
]),
Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]), Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
} }
} }
@ -2699,7 +2430,6 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
Size, Size,
StaticType: None, StaticType: None,
Type: None, Type: None,
PhantomConstGet: None,
Unknown: vec![], Unknown: vec![],
} }
} }
@ -2802,9 +2532,6 @@ impl ParsedBound {
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
Self::PhantomConstGet(v) => {
ParsedBoundCategory::Type(ParsedTypeBound::PhantomConstGet(v))
}
Self::Unknown(v) => ParsedBoundCategory::Unknown(v), Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
} }
} }
@ -3023,7 +2750,7 @@ impl ParsedGenerics {
let span = ident.span(); let span = ident.span();
ItemStruct { ItemStruct {
attrs: vec![ attrs: vec![
common_derives(span, true), common_derives(span),
parse_quote_spanned! {span=> parse_quote_spanned! {span=>
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
}, },
@ -3690,8 +3417,7 @@ impl ParsedGenerics {
| ParsedTypeBound::BundleType(_) | ParsedTypeBound::BundleType(_)
| ParsedTypeBound::EnumType(_) | ParsedTypeBound::EnumType(_)
| ParsedTypeBound::IntType(_) | ParsedTypeBound::IntType(_)
| ParsedTypeBound::ResetType(_) | ParsedTypeBound::ResetType(_) => {
| ParsedTypeBound::PhantomConstGet(_) => {
errors.error(bound, "bounds on mask types are not implemented"); errors.error(bound, "bounds on mask types are not implemented");
} }
ParsedTypeBound::StaticType(bound) => { ParsedTypeBound::StaticType(bound) => {
@ -4660,230 +4386,3 @@ 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<ParsedSimpleVisibility> 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<std::cmp::Ordering> {
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<Self, syn::VisRestricted> {
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<ParsedVisibility> 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<std::cmp::Ordering> {
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 })
}
}
pub(crate) struct CustomDebugTrait<'a> {
pub(crate) trait_path: &'a Path,
pub(crate) fn_name: &'a Ident,
pub(crate) this_arg: &'a FnArg,
}
#[must_use]
pub(crate) fn create_struct_debug_impl(
item_struct: &ItemStruct,
debug_struct_name: &str,
custom_debug_trait: Option<CustomDebugTrait<'_>>,
) -> TokenStream {
let ident = &item_struct.ident;
let span = ident.span();
let (impl_generics, type_generics, where_clause) = item_struct.generics.split_for_impl();
let trait_path;
let fn_name;
let this_arg;
let CustomDebugTrait {
trait_path,
fn_name,
this_arg,
} = match custom_debug_trait {
Some(v) => v,
None => {
trait_path = parse_quote_spanned! {span=>
::fayalite::__std::fmt::Debug
};
fn_name = parse_quote_spanned! {span=>
fmt
};
this_arg = parse_quote_spanned! {span=>
&self
};
CustomDebugTrait {
trait_path: &trait_path,
fn_name: &fn_name,
this_arg: &this_arg,
}
}
};
let this_arg_name = match this_arg {
FnArg::Receiver(this_arg) => this_arg.self_token.to_token_stream(),
FnArg::Typed(this_arg) => match &*this_arg.pat {
syn::Pat::Ident(pat_ident) => pat_ident.ident.to_token_stream(),
_ => unreachable!(),
},
};
match &item_struct.fields {
Fields::Named(fields) => {
let field_idents = fields
.named
.iter()
.map(|v| v.ident.as_ref().expect("known to have field name"));
let field_names = field_idents.clone().map(|v| v.to_string());
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics #trait_path for #ident #type_generics
#where_clause
{
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
let _ = #this_arg_name;
f.debug_struct(#debug_struct_name)
#(.field(#field_names, &#this_arg_name.#field_idents))*
.finish()
}
}
}
}
Fields::Unnamed(fields) => {
let field_members = fields
.unnamed
.iter()
.enumerate()
.map(|(index, _)| syn::Index {
index: index as _,
span,
});
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics #trait_path for #ident #type_generics
#where_clause
{
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
let _ = #this_arg_name;
f.debug_tuple(#debug_struct_name)
#(.field(&#this_arg_name.#field_members))*
.finish()
}
}
}
}
Fields::Unit => quote_spanned! {ident.span()=>
#[automatically_derived]
impl #impl_generics #trait_path for #ident #type_generics
#where_clause
{
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
let _ = #this_arg_name;
f.write_str(#debug_struct_name)
}
}
},
}
}

View file

@ -42,7 +42,6 @@ pub(crate) trait CustomToken:
mod kw { mod kw {
pub(crate) use syn::token::Extern as extern_; pub(crate) use syn::token::Extern as extern_;
pub(crate) use syn::token::Type as type_;
macro_rules! custom_keyword { macro_rules! custom_keyword {
($kw:ident) => { ($kw:ident) => {
@ -67,7 +66,6 @@ mod kw {
} }
custom_keyword!(__evaluated_cfgs); custom_keyword!(__evaluated_cfgs);
custom_keyword!(add_platform_io);
custom_keyword!(all); custom_keyword!(all);
custom_keyword!(any); custom_keyword!(any);
custom_keyword!(cfg); custom_keyword!(cfg);
@ -76,18 +74,13 @@ mod kw {
custom_keyword!(cmp_eq); custom_keyword!(cmp_eq);
custom_keyword!(connect_inexact); custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds); custom_keyword!(custom_bounds);
custom_keyword!(custom_debug);
custom_keyword!(custom_sim_display);
custom_keyword!(flip); custom_keyword!(flip);
custom_keyword!(get);
custom_keyword!(hdl); custom_keyword!(hdl);
custom_keyword!(hdl_module); custom_keyword!(hdl_module);
custom_keyword!(incomplete_wire); custom_keyword!(incomplete_wire);
custom_keyword!(input); custom_keyword!(input);
custom_keyword!(instance); custom_keyword!(instance);
custom_keyword!(m); custom_keyword!(m);
custom_keyword!(mask_sim);
custom_keyword!(mask_type);
custom_keyword!(memory); custom_keyword!(memory);
custom_keyword!(memory_array); custom_keyword!(memory_array);
custom_keyword!(memory_with_init); custom_keyword!(memory_with_init);
@ -892,13 +885,7 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
} }
} }
let _print_on_panic = PrintOnPanic(&contents); let _print_on_panic = PrintOnPanic(&contents);
let mut parse_err = None; let contents = prettyplease::unparse(&parse_quote! { #contents });
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 = <sha2::Sha256 as sha2::Digest>::digest(&contents); let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
let hash = base16ct::HexDisplay(&hash[..5]); let hash = base16ct::HexDisplay(&hash[..5]);
file.write_all(contents.as_bytes()).unwrap(); file.write_all(contents.as_bytes()).unwrap();
@ -910,26 +897,9 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
e.unwrap(); e.unwrap();
} }
} }
let log_msg = if let Some(parse_err) = parse_err { eprintln!("generated {}", dest_file.display());
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(); let dest_file = dest_file.to_str().unwrap();
quote! { quote! {
include!(#dest_file); include!(#dest_file);
} }

View file

@ -4,7 +4,7 @@ use crate::{
Errors, HdlAttr, PairsIterExt, Errors, HdlAttr, PairsIterExt,
hdl_type_common::{ParsedGenerics, SplitForImpl}, hdl_type_common::{ParsedGenerics, SplitForImpl},
kw, kw,
module::transform_body::{HdlLet, HdlLetKindIO, ModuleIOOrAddPlatformIO}, module::transform_body::{HdlLet, HdlLetKindIO},
options, options,
}; };
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
@ -39,7 +39,7 @@ pub(crate) fn check_name_conflicts_with_module_builder(name: &Ident) -> syn::Res
if name == "m" { if name == "m" {
Err(Error::new_spanned( Err(Error::new_spanned(
name, name,
"name conflicts with implicit `m: &ModuleBuilder`", "name conflicts with implicit `m: &mut ModuleBuilder<_>`",
)) ))
} else { } else {
Ok(()) Ok(())
@ -67,7 +67,7 @@ struct ModuleFnModule {
vis: Visibility, vis: Visibility,
sig: Signature, sig: Signature,
block: Box<Block>, block: Box<Block>,
struct_generics: Option<ParsedGenerics>, struct_generics: ParsedGenerics,
the_struct: TokenStream, the_struct: TokenStream,
} }
@ -290,7 +290,7 @@ impl ModuleFn {
paren_token, paren_token,
body, body,
} => { } => {
debug_assert!(matches!(io, ModuleIOOrAddPlatformIO::ModuleIO(v) if v.is_empty())); debug_assert!(io.is_empty());
return Ok(Self(ModuleFnImpl::Fn { return Ok(Self(ModuleFnImpl::Fn {
attrs, attrs,
config_options: HdlAttr { config_options: HdlAttr {
@ -322,21 +322,6 @@ impl ModuleFn {
body, 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) = let (_struct_impl_generics, _struct_type_generics, struct_where_clause) =
struct_generics.split_for_impl(); struct_generics.split_for_impl();
let struct_where_clause: Option<WhereClause> = parse_quote! { #struct_where_clause }; let struct_where_clause: Option<WhereClause> = parse_quote! { #struct_where_clause };
@ -379,7 +364,7 @@ impl ModuleFn {
vis, vis,
sig, sig,
block, block,
struct_generics: Some(struct_generics), struct_generics,
the_struct, the_struct,
}))) })))
} }
@ -448,14 +433,9 @@ impl ModuleFn {
ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal }, ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal },
}; };
let fn_name = &outer_sig.ident; let fn_name = &outer_sig.ident;
let struct_ty = match struct_generics {
Some(struct_generics) => {
let (_struct_impl_generics, struct_type_generics, _struct_where_clause) = let (_struct_impl_generics, struct_type_generics, _struct_where_clause) =
struct_generics.split_for_impl(); struct_generics.split_for_impl();
quote! {#fn_name #struct_type_generics} let struct_ty = quote! {#fn_name #struct_type_generics};
}
None => quote! {::fayalite::bundle::Bundle},
};
body_sig.ident = parse_quote! {__body}; body_sig.ident = parse_quote! {__body};
body_sig body_sig
.inputs .inputs

View file

@ -39,7 +39,6 @@ options! {
pub(crate) enum LetFnKind { pub(crate) enum LetFnKind {
Input(input), Input(input),
Output(output), Output(output),
AddPlatformIO(add_platform_io),
Instance(instance), Instance(instance),
RegBuilder(reg_builder), RegBuilder(reg_builder),
Wire(wire), Wire(wire),
@ -217,49 +216,6 @@ 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<Expr>,
}
impl ParseTypes<Self> for HdlLetKindAddPlatformIO {
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
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<Expr>,
}
}
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)] #[derive(Clone, Debug)]
pub(crate) struct RegBuilderClockDomain { pub(crate) struct RegBuilderClockDomain {
pub(crate) dot_token: Token![.], pub(crate) dot_token: Token![.],
@ -755,7 +711,6 @@ impl HdlLetKindMemory {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) enum HdlLetKind<IOType = ParsedType> { pub(crate) enum HdlLetKind<IOType = ParsedType> {
IO(HdlLetKindIO<ModuleIOKind, IOType>), IO(HdlLetKindIO<ModuleIOKind, IOType>),
AddPlatformIO(HdlLetKindAddPlatformIO),
Incomplete(HdlLetKindIncomplete), Incomplete(HdlLetKindIncomplete),
Instance(HdlLetKindInstance), Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder), RegBuilder(HdlLetKindRegBuilder),
@ -766,7 +721,6 @@ pub(crate) enum HdlLetKind<IOType = ParsedType> {
impl_fold! { impl_fold! {
enum HdlLetKind<IOType,> { enum HdlLetKind<IOType,> {
IO(HdlLetKindIO<ModuleIOKind, IOType>), IO(HdlLetKindIO<ModuleIOKind, IOType>),
AddPlatformIO(HdlLetKindAddPlatformIO),
Incomplete(HdlLetKindIncomplete), Incomplete(HdlLetKindIncomplete),
Instance(HdlLetKindInstance), Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder), RegBuilder(HdlLetKindRegBuilder),
@ -782,9 +736,6 @@ impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> {
) -> Result<Self, ParseFailed> { ) -> Result<Self, ParseFailed> {
match input { match input {
HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO), 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) => { HdlLetKind::Incomplete(input) => {
ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete) ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete)
} }
@ -910,23 +861,6 @@ impl HdlLetKindParse for HdlLetKind<Type> {
ModuleIOKind::Output(output), ModuleIOKind::Output(output),
) )
.map(Self::IO), .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,)) => { LetFnKind::Instance((instance,)) => {
if let Some(parsed_ty) = parsed_ty { if let Some(parsed_ty) = parsed_ty {
return Err(Error::new_spanned( return Err(Error::new_spanned(
@ -1002,7 +936,6 @@ impl HdlLetKindToTokens for HdlLetKind {
fn ty_to_tokens(&self, tokens: &mut TokenStream) { fn ty_to_tokens(&self, tokens: &mut TokenStream) {
match self { match self {
HdlLetKind::IO(v) => v.ty_to_tokens(tokens), 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::Incomplete(v) => v.ty_to_tokens(tokens),
HdlLetKind::Instance(v) => v.ty_to_tokens(tokens), HdlLetKind::Instance(v) => v.ty_to_tokens(tokens),
HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens), HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens),
@ -1014,7 +947,6 @@ impl HdlLetKindToTokens for HdlLetKind {
fn expr_to_tokens(&self, tokens: &mut TokenStream) { fn expr_to_tokens(&self, tokens: &mut TokenStream) {
match self { match self {
HdlLetKind::IO(v) => v.expr_to_tokens(tokens), 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::Incomplete(v) => v.expr_to_tokens(tokens),
HdlLetKind::Instance(v) => v.expr_to_tokens(tokens), HdlLetKind::Instance(v) => v.expr_to_tokens(tokens),
HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens), HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens),
@ -1217,7 +1149,7 @@ impl<T: ToString> ToTokens for ImplicitName<T> {
struct Visitor<'a> { struct Visitor<'a> {
module_kind: Option<ModuleKind>, module_kind: Option<ModuleKind>,
errors: Errors, errors: Errors,
io: ModuleIOOrAddPlatformIO, io: Vec<ModuleIO>,
block_depth: usize, block_depth: usize,
parsed_generics: &'a ParsedGenerics, parsed_generics: &'a ParsedGenerics,
} }
@ -1357,81 +1289,7 @@ impl Visitor<'_> {
}), }),
semi_token: hdl_let.semi_token, semi_token: hdl_let.semi_token,
}; };
match &mut self.io { self.io.push(hdl_let);
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<HdlLetKindAddPlatformIO>,
) -> 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 let_stmt
} }
fn process_hdl_let_instance(&mut self, hdl_let: HdlLet<HdlLetKindInstance>) -> Local { fn process_hdl_let_instance(&mut self, hdl_let: HdlLet<HdlLetKindInstance>) -> Local {
@ -1652,7 +1510,6 @@ impl Visitor<'_> {
} }
the_match! { the_match! {
IO => process_hdl_let_io, IO => process_hdl_let_io,
AddPlatformIO => process_hdl_let_add_platform_io,
Incomplete => process_hdl_let_incomplete, Incomplete => process_hdl_let_incomplete,
Instance => process_hdl_let_instance, Instance => process_hdl_let_instance,
RegBuilder => process_hdl_let_reg_builder, RegBuilder => process_hdl_let_reg_builder,
@ -1896,20 +1753,15 @@ impl Fold for Visitor<'_> {
} }
} }
pub(crate) enum ModuleIOOrAddPlatformIO {
ModuleIO(Vec<ModuleIO>),
AddPlatformIO,
}
pub(crate) fn transform_body( pub(crate) fn transform_body(
module_kind: Option<ModuleKind>, module_kind: Option<ModuleKind>,
mut body: Box<Block>, mut body: Box<Block>,
parsed_generics: &ParsedGenerics, parsed_generics: &ParsedGenerics,
) -> syn::Result<(Box<Block>, ModuleIOOrAddPlatformIO)> { ) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {
let mut visitor = Visitor { let mut visitor = Visitor {
module_kind, module_kind,
errors: Errors::new(), errors: Errors::new(),
io: ModuleIOOrAddPlatformIO::ModuleIO(vec![]), io: vec![],
block_depth: 0, block_depth: 0,
parsed_generics, parsed_generics,
}; };

View file

@ -88,9 +88,6 @@ impl Visitor<'_> {
field.expr = parse_quote_spanned! {field.member.span()=> field.expr = parse_quote_spanned! {field.member.span()=>
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)) ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
}; };
field
.colon_token
.get_or_insert(Token![:](field.member.span()));
} }
return parse_quote_spanned! {name_span=> return parse_quote_spanned! {name_span=>
{ {

View file

@ -10,7 +10,7 @@ use crate::{
}; };
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned}; use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned};
use std::collections::BTreeMap; use std::collections::BTreeSet;
use syn::{ use syn::{
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr, Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment, PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
@ -24,65 +24,65 @@ use syn::{
macro_rules! visit_trait { macro_rules! visit_trait {
( (
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &mut $Value:ty) $block:block)* $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)*
) => { ) => {
trait VisitMatchPat<'a> { trait VisitMatchPat<'a> {
$(fn $fn(&mut self, $value: &'a mut $Value) { $(fn $fn(&mut self, $value: &'a $Value) {
$fn(self, $value); $fn(self, $value);
})* })*
} }
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a mut $Value) $block)* $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)*
}; };
} }
visit_trait! { visit_trait! {
fn visit_match_pat_binding(_state: _, v: &mut MatchPatBinding) { fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) {
let MatchPatBinding { mutability: _, ident: _ } = v; let MatchPatBinding { ident: _ } = v;
} }
fn visit_match_pat_wild(_state: _, v: &mut MatchPatWild) { fn visit_match_pat_wild(_state: _, v: &MatchPatWild) {
let MatchPatWild { underscore_token: _ } = v; let MatchPatWild { underscore_token: _ } = v;
} }
fn visit_match_pat_rest(_state: _, v: &mut MatchPatRest) { fn visit_match_pat_rest(_state: _, v: &MatchPatRest) {
let MatchPatRest { dot2_token: _ } = v; let MatchPatRest { dot2_token: _ } = v;
} }
fn visit_match_pat_paren(state: _, v: &mut MatchPatParen<MatchPat>) { fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) {
let MatchPatParen { paren_token: _, pat } = v; let MatchPatParen { paren_token: _, pat } = v;
state.visit_match_pat(pat); state.visit_match_pat(pat);
} }
fn visit_match_pat_paren_simple(state: _, v: &mut MatchPatParen<MatchPatSimple>) { fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) {
let MatchPatParen { paren_token: _, pat } = v; let MatchPatParen { paren_token: _, pat } = v;
state.visit_match_pat_simple(pat); state.visit_match_pat_simple(pat);
} }
fn visit_match_pat_or(state: _, v: &mut MatchPatOr<MatchPat>) { fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) {
let MatchPatOr { leading_vert: _, cases } = v; let MatchPatOr { leading_vert: _, cases } = v;
for v in cases { for v in cases {
state.visit_match_pat(v); state.visit_match_pat(v);
} }
} }
fn visit_match_pat_or_simple(state: _, v: &mut MatchPatOr<MatchPatSimple>) { fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) {
let MatchPatOr { leading_vert: _, cases } = v; let MatchPatOr { leading_vert: _, cases } = v;
for v in cases { for v in cases {
state.visit_match_pat_simple(v); state.visit_match_pat_simple(v);
} }
} }
fn visit_match_pat_struct_field(state: _, v: &mut MatchPatStructField) { fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) {
let MatchPatStructField { field_name: _, colon_token: _, pat } = v; let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
state.visit_match_pat_simple(pat); state.visit_match_pat_simple(pat);
} }
fn visit_match_pat_struct(state: _, v: &mut MatchPatStruct) { fn visit_match_pat_struct(state: _, v: &MatchPatStruct) {
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v; let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
for v in fields { for v in fields {
state.visit_match_pat_struct_field(v); state.visit_match_pat_struct_field(v);
} }
} }
fn visit_match_pat_tuple(state: _, v: &mut MatchPatTuple) { fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) {
let MatchPatTuple { paren_token: _, fields } = v; let MatchPatTuple { paren_token: _, fields } = v;
for v in fields { for v in fields {
state.visit_match_pat_simple(v); state.visit_match_pat_simple(v);
} }
} }
fn visit_match_pat_enum_variant(state: _, v: &mut MatchPatEnumVariant) { fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) {
let MatchPatEnumVariant { let MatchPatEnumVariant {
match_span:_, match_span:_,
sim:_, sim:_,
@ -95,7 +95,7 @@ visit_trait! {
state.visit_match_pat_simple(v); state.visit_match_pat_simple(v);
} }
} }
fn visit_match_pat_simple(state: _, v: &mut MatchPatSimple) { fn visit_match_pat_simple(state: _, v: &MatchPatSimple) {
match v { match v {
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v), MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
MatchPatSimple::Or(v) => state.visit_match_pat_or_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), MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v),
} }
} }
fn visit_match_pat(state: _, v: &mut MatchPat) { fn visit_match_pat(state: _, v: &MatchPat) {
match v { match v {
MatchPat::Simple(v) => state.visit_match_pat_simple(v), MatchPat::Simple(v) => state.visit_match_pat_simple(v),
MatchPat::Or(v) => state.visit_match_pat_or(v), MatchPat::Or(v) => state.visit_match_pat_or(v),
@ -118,15 +118,13 @@ visit_trait! {
with_debug_clone_and_fold! { with_debug_clone_and_fold! {
struct MatchPatBinding<> { struct MatchPatBinding<> {
mutability: Option<Token![mut]>,
ident: Ident, ident: Ident,
} }
} }
impl ToTokens for MatchPatBinding { impl ToTokens for MatchPatBinding {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let Self { mutability, ident } = self; let Self { ident } = self;
mutability.to_tokens(tokens);
ident.to_tokens(tokens); ident.to_tokens(tokens);
} }
} }
@ -213,20 +211,12 @@ impl ToTokens for MatchPatStructField {
colon_token, colon_token,
pat, pat,
} = self; } = self;
if let ( field_name.to_tokens(tokens);
None, if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
MatchPatSimple::Binding(MatchPatBinding {
mutability: _,
ident,
}),
) = (colon_token, pat)
{
if field_name == ident { if field_name == ident {
pat.to_tokens(tokens);
return; return;
} }
} }
field_name.to_tokens(tokens);
colon_token colon_token
.unwrap_or_else(|| Token![:](field_name.span())) .unwrap_or_else(|| Token![:](field_name.span()))
.to_tokens(tokens); .to_tokens(tokens);
@ -460,7 +450,7 @@ trait ParseMatchPat: Sized {
Pat::Ident(PatIdent { Pat::Ident(PatIdent {
attrs: _, attrs: _,
by_ref, by_ref,
mut mutability, mutability,
ident, ident,
subpat, subpat,
}) => { }) => {
@ -469,13 +459,10 @@ trait ParseMatchPat: Sized {
.errors .errors
.error(by_ref, "ref not allowed in #[hdl] patterns"); .error(by_ref, "ref not allowed in #[hdl] patterns");
} }
if let Some(mut_token) = mutability { if let Some(mutability) = mutability {
if state.sim.is_none() {
state state
.errors .errors
.error(mut_token, "mut not allowed in #[hdl] patterns"); .error(mutability, "mut not allowed in #[hdl] patterns");
mutability = None; // avoid duplicate errors
}
} }
if let Some((at_token, _)) = subpat { if let Some((at_token, _)) = subpat {
state state
@ -487,13 +474,7 @@ trait ParseMatchPat: Sized {
variant_path, variant_path,
enum_path, enum_path,
variant_name, variant_name,
}) => { }) => Self::enum_variant(
if let Some(mut_token) = mutability {
state
.errors
.error(mut_token, "mut not allowed on unit variants");
}
Self::enum_variant(
state, state,
MatchPatEnumVariant { MatchPatEnumVariant {
match_span: state.match_span, match_span: state.match_span,
@ -503,10 +484,8 @@ trait ParseMatchPat: Sized {
variant_name, variant_name,
field: None, field: None,
}, },
) ),
}
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding { Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
mutability,
ident, ident,
}))), }))),
} }
@ -1001,16 +980,15 @@ struct HdlMatchParseState<'a> {
struct HdlLetPatVisitState<'a> { struct HdlLetPatVisitState<'a> {
errors: &'a mut Errors, errors: &'a mut Errors,
bindings: BTreeMap<Ident, MatchPatBinding>, bindings: BTreeSet<&'a Ident>,
} }
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
fn visit_match_pat_binding(&mut self, v: &'a mut MatchPatBinding) { fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) {
self.bindings.insert(v.ident.clone(), v.clone()); self.bindings.insert(&v.ident);
v.mutability = None;
} }
fn visit_match_pat_or(&mut self, v: &'a mut MatchPatOr<MatchPat>) { fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) {
if let Some(first_inner_vert) = v.first_inner_vert() { if let Some(first_inner_vert) = v.first_inner_vert() {
self.errors.error( self.errors.error(
first_inner_vert, first_inner_vert,
@ -1020,7 +998,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
visit_match_pat_or(self, v); visit_match_pat_or(self, v);
} }
fn visit_match_pat_or_simple(&mut self, v: &'a mut MatchPatOr<MatchPatSimple>) { fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) {
if let Some(first_inner_vert) = v.first_inner_vert() { if let Some(first_inner_vert) = v.first_inner_vert() {
self.errors.error( self.errors.error(
first_inner_vert, first_inner_vert,
@ -1030,7 +1008,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
visit_match_pat_or_simple(self, v); visit_match_pat_or_simple(self, v);
} }
fn visit_match_pat_enum_variant(&mut self, v: &'a mut MatchPatEnumVariant) { fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) {
self.errors.error(v, "refutable pattern in let statement"); self.errors.error(v, "refutable pattern in let statement");
} }
} }
@ -1070,7 +1048,7 @@ impl Visitor<'_> {
.error(else_, "#[hdl] let ... else { ... } is not implemented"); .error(else_, "#[hdl] let ... else { ... } is not implemented");
return empty_let(); return empty_let();
} }
let Ok(mut pat) = MatchPat::parse( let Ok(pat) = MatchPat::parse(
&mut HdlMatchParseState { &mut HdlMatchParseState {
sim, sim,
match_span: span, match_span: span,
@ -1082,25 +1060,20 @@ impl Visitor<'_> {
}; };
let mut state = HdlLetPatVisitState { let mut state = HdlLetPatVisitState {
errors: &mut self.errors, errors: &mut self.errors,
bindings: BTreeMap::new(), bindings: BTreeSet::new(),
}; };
state.visit_match_pat(&mut pat); state.visit_match_pat(&pat);
let HdlLetPatVisitState { let HdlLetPatVisitState {
errors: _, errors: _,
bindings, bindings,
} = state; } = state;
let bindings_idents = bindings.keys();
let bindings = bindings.values();
let retval = if sim.is_some() { let retval = if sim.is_some() {
parse_quote_spanned! {span=> parse_quote_spanned! {span=>
let (#(#bindings,)*) = { let (#(#bindings,)*) = {
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue; type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
let __match_value = #expr; let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
// use method syntax to deduce what type to convert to #let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token
let __match_value = ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value) (#(#bindings,)*)
.__fayalite_match_sim_value();
#let_token #pat #eq_token __match_value #semi_token
(#(#bindings_idents,)*)
}; };
} }
} else { } else {
@ -1132,7 +1105,7 @@ impl Visitor<'_> {
__match_variant, __match_variant,
); );
#let_token #pat #eq_token __match_variant #semi_token #let_token #pat #eq_token __match_variant #semi_token
(#(#bindings_idents,)* __scope,) (#(#bindings,)* __scope,)
}; };
} }
}; };
@ -1169,11 +1142,8 @@ impl Visitor<'_> {
quote_spanned! {span=> quote_spanned! {span=>
{ {
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue; type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
let __match_value = #expr; let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
// use method syntax to deduce what type to convert to #match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) {
let __match_value = ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value)
.__fayalite_match_sim_value();
#match_token __match_value {
#(#arms)* #(#arms)*
} }
} }

View file

@ -14,11 +14,9 @@ rust-version.workspace = true
version.workspace = true version.workspace = true
[dependencies] [dependencies]
base64.workspace = true
bitvec.workspace = true bitvec.workspace = true
blake3.workspace = true blake3.workspace = true
clap.workspace = true clap.workspace = true
clap_complete.workspace = true
ctor.workspace = true ctor.workspace = true
eyre.workspace = true eyre.workspace = true
fayalite-proc-macros.workspace = true fayalite-proc-macros.workspace = true
@ -26,12 +24,10 @@ hashbrown.workspace = true
jobslot.workspace = true jobslot.workspace = true
num-bigint.workspace = true num-bigint.workspace = true
num-traits.workspace = true num-traits.workspace = true
once_cell.workspace = true os_pipe.workspace = true
ordered-float.workspace = true
petgraph.workspace = true petgraph.workspace = true
serde_json.workspace = true serde_json.workspace = true
serde.workspace = true serde.workspace = true
sha2.workspace = true
tempfile.workspace = true tempfile.workspace = true
vec_map.workspace = true vec_map.workspace = true
which.workspace = true which.workspace = true

View file

@ -1,64 +1,47 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use fayalite::prelude::*; use clap::Parser;
use fayalite::{cli, prelude::*};
#[hdl_module] #[hdl_module]
fn blinky(platform_io_builder: PlatformIOBuilder<'_>) { fn blinky(clock_frequency: u64) {
let clk_input = #[hdl]
platform_io_builder.peripherals_with_type::<peripherals::ClockInput>()[0].use_peripheral(); let clk: Clock = m.input();
let rst = platform_io_builder.peripherals_with_type::<Reset>()[0].use_peripheral(); #[hdl]
let rst: SyncReset = m.input();
let cd = #[hdl] let cd = #[hdl]
ClockDomain { ClockDomain {
clk: clk_input.clk, clk,
rst, rst: rst.to_reset(),
}; };
let max_value = (clk_input.ty().frequency() / 2.0).round_ties_even() as u64 - 1; let max_value = clock_frequency / 2 - 1;
let int_ty = UInt::range_inclusive(0..=max_value); let int_ty = UInt::range_inclusive(0..=max_value);
#[hdl] #[hdl]
let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty)); let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty));
#[hdl] #[hdl]
let output_reg: Bool = reg_builder().clock_domain(cd).reset(false); let output_reg: Bool = reg_builder().clock_domain(cd).reset(false);
#[hdl] #[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) { if counter_reg.cmp_eq(max_value) {
connect_any(counter_reg, 0u8); connect_any(counter_reg, 0u8);
connect(output_reg, !output_reg); 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 { } else {
connect_any(counter_reg, counter_reg + 1_hdl_u1); connect_any(counter_reg, counter_reg + 1_hdl_u1);
} }
for led in platform_io_builder.peripherals_with_type::<peripherals::Led>() {
if let Ok(led) = led.try_use_peripheral() {
connect(led.on, output_reg);
}
}
for rgb_led in platform_io_builder.peripherals_with_type::<peripherals::RgbLed>() {
if let Ok(rgb_led) = rgb_led.try_use_peripheral() {
connect(rgb_led, rgb_output_reg);
}
}
#[hdl] #[hdl]
let io = m.add_platform_io(platform_io_builder); let led: Bool = m.output();
connect(led, output_reg);
} }
fn main() { #[derive(Parser)]
<BuildCli>::main("blinky", |_, platform, _| { struct Cli {
Ok(JobParams::new(platform.wrap_main_module(blinky))) /// 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))
} }

View file

@ -1,188 +0,0 @@
// 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::<peripherals::ClockInput>();
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::<Reset>()[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<UInt<8>> = 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<Bool, 10> = 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::<peripherals::Uart>() {
connect(uart.use_peripheral().tx, tx);
}
#[hdl]
let io = m.add_platform_io(platform_io_builder);
}
fn parse_baud_rate(
v: impl AsRef<str>,
) -> Result<NotNan<f64>, Box<dyn std::error::Error + Send + Sync>> {
let retval: NotNan<f64> = 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<f64>,
#[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<ExtraArgs>;
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| {
<Cli as clap::CommandFactory>::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))
})?))
},
);
}

View file

@ -31,114 +31,3 @@
//! } //! }
//! } //! }
//! ``` //! ```
//!
//! 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<T> {
//! a: UInt<8>,
//! b: Bool,
//! c: T,
//! }
//!
//! #[hdl]
//! fn destructure<T: Type>(v: SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! mut b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: SimValue<UInt<8>> = a;
//! let _: SimValue<Bool> = b;
//! let _: SimValue<T> = c;
//! *b = false; // can modify b since mut was used
//! }
//!
//! #[hdl]
//! fn destructure_ref<'a, T: Type>(v: &'a SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: &'a SimValue<UInt<8>> = a;
//! let _: &'a SimValue<Bool> = b;
//! let _: &'a SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_mut<'a, T: Type>(v: &'a mut SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! 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<UInt<8>> = a;
//! let _: &'a mut SimValue<Bool> = b;
//! let _: &'a mut SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_inner<T: Type>(v: <MyStruct<T> as Type>::SimValue) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! mut b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: SimValue<UInt<8>> = a;
//! let _: SimValue<Bool> = b;
//! let _: SimValue<T> = c;
//! *b = false; // can modify b since mut was used
//! }
//!
//! #[hdl]
//! fn destructure_inner_ref<'a, T: Type>(v: &'a <MyStruct<T> as Type>::SimValue) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: &'a SimValue<UInt<8>> = a;
//! let _: &'a SimValue<Bool> = b;
//! let _: &'a SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_inner_mut<'a, T: Type>(v: &'a mut <MyStruct<T> as Type>::SimValue) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! 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<UInt<8>> = a;
//! let _: &'a mut SimValue<Bool> = b;
//! let _: &'a mut SimValue<T> = c;
//! }
//! ```

View file

@ -9,110 +9,3 @@
//! //!
//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now, //! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now,
//! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`. //! 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<T> {
//! A,
//! B(Bool),
//! C(T),
//! }
//!
//! #[hdl]
//! fn match_move<T: Type>(v: SimValue<MyEnum<T>>) -> String {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => String::from("got A"),
//! MyEnum::<T>::B(mut b) => {
//! let _: SimValue<Bool> = 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<MyEnum<T>>) -> u32 {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => 1,
//! MyEnum::<T>::B(b) => {
//! let _: &'a SimValue<Bool> = b; // b has this type
//! println!("got B({b})");
//! 5
//! }
//! _ => 42,
//! }
//! }
//!
//! #[hdl]
//! fn match_mut<'a, T: Type>(v: &'a mut SimValue<MyEnum<T>>) -> Option<&'a mut SimValue<T>> {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => None,
//! MyEnum::<T>::B(b) => {
//! println!("got B({b})");
//! **b = true; // you can modify v by modifying b which borrows from it
//! let _: &'a mut SimValue<Bool> = b; // b has this type
//! None
//! }
//! MyEnum::<T>::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_inner_move<T: Type>(v: <MyEnum<T> as Type>::SimValue) -> String {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => String::from("got A"),
//! MyEnum::<T>::B(mut b) => {
//! let _: SimValue<Bool> = 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_inner_ref<'a, T: Type>(v: &'a <MyEnum<T> as Type>::SimValue) -> u32 {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => 1,
//! MyEnum::<T>::B(b) => {
//! let _: &'a SimValue<Bool> = b; // b has this type
//! println!("got B({b})");
//! 5
//! }
//! _ => 42,
//! }
//! }
//!
//! #[hdl]
//! fn match_inner_mut<'a, T: Type>(v: &'a mut <MyEnum<T> as Type>::SimValue) -> Option<&'a mut SimValue<T>> {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => None,
//! MyEnum::<T>::B(b) => {
//! println!("got B({b})");
//! **b = true; // you can modify v by modifying b which borrows from it
//! let _: &'a mut SimValue<Bool> = b; // b has this type
//! None
//! }
//! MyEnum::<T>::C(v) => Some(v), // you can return matched values
//! _ => None, // HDL enums can have invalid discriminants, so we need this extra match arm
//! }
//! }
//! ```

View file

@ -145,73 +145,52 @@ pub struct DocStringAnnotation {
macro_rules! make_annotation_enum { macro_rules! make_annotation_enum {
( (
#[$non_exhaustive:ident]
$(#[$meta:meta])* $(#[$meta:meta])*
$vis:vis enum $AnnotationEnum:ident { $vis:vis enum $Annotation:ident {
$($Variant:ident($T:ty),)* $($Variant:ident($T:ident),)*
} }
) => { ) => {
crate::annotations::make_annotation_enum!(@require_non_exhaustive $non_exhaustive);
#[$non_exhaustive]
$(#[$meta])* $(#[$meta])*
#[derive(Clone, PartialEq, Eq, Hash)] $vis enum $Annotation {
$vis enum $AnnotationEnum {
$($Variant($T),)* $($Variant($T),)*
} }
impl std::fmt::Debug for $AnnotationEnum { $(impl IntoAnnotations for $T {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { type IntoAnnotations = [$Annotation; 1];
match self {
$(Self::$Variant(v) => v.fmt(f),)*
}
}
}
$(impl From<$T> for crate::annotations::Annotation {
fn from(v: $T) -> Self {
$AnnotationEnum::$Variant(v).into()
}
}
impl crate::annotations::IntoAnnotations for $T {
type IntoAnnotations = [crate::annotations::Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations { fn into_annotations(self) -> Self::IntoAnnotations {
[self.into()] [$Annotation::$Variant(self)]
} }
} }
impl crate::annotations::IntoAnnotations for &'_ $T { impl IntoAnnotations for &'_ $T {
type IntoAnnotations = [crate::annotations::Annotation; 1]; type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations { fn into_annotations(self) -> Self::IntoAnnotations {
[crate::annotations::Annotation::from(self.clone())] [$Annotation::$Variant(*self)]
} }
} }
impl crate::annotations::IntoAnnotations for &'_ mut $T { impl IntoAnnotations for &'_ mut $T {
type IntoAnnotations = [crate::annotations::Annotation; 1]; type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations { fn into_annotations(self) -> Self::IntoAnnotations {
[crate::annotations::Annotation::from(self.clone())] [$Annotation::$Variant(*self)]
} }
} }
impl crate::annotations::IntoAnnotations for Box<$T> { impl IntoAnnotations for Box<$T> {
type IntoAnnotations = [crate::annotations::Annotation; 1]; type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations { fn into_annotations(self) -> Self::IntoAnnotations {
[crate::annotations::Annotation::from(*self)] [$Annotation::$Variant(*self)]
} }
})* })*
}; };
(@require_non_exhaustive non_exhaustive) => {};
} }
pub(crate) use make_annotation_enum;
make_annotation_enum! { make_annotation_enum! {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub enum Annotation { pub enum Annotation {
DontTouch(DontTouchAnnotation), DontTouch(DontTouchAnnotation),
@ -220,7 +199,6 @@ make_annotation_enum! {
BlackBoxPath(BlackBoxPathAnnotation), BlackBoxPath(BlackBoxPathAnnotation),
DocString(DocStringAnnotation), DocString(DocStringAnnotation),
CustomFirrtl(CustomFirrtlAnnotation), CustomFirrtl(CustomFirrtlAnnotation),
Xilinx(crate::vendor::xilinx::XilinxAnnotation),
} }
} }
@ -238,10 +216,7 @@ impl TargetedAnnotation {
} }
#[track_caller] #[track_caller]
pub fn assert_valid_target(target: Interned<Target>) { pub fn assert_valid_target(target: Interned<Target>) {
assert!( assert!(target.is_static(), "can't annotate non-static targets");
target.is_valid_annotation_target(),
"not a valid annotation target: {target:?}",
);
} }
pub fn target(&self) -> Interned<Target> { pub fn target(&self) -> Interned<Target> {
self.target self.target

View file

@ -3,23 +3,23 @@
use crate::{ use crate::{
expr::{ expr::{
CastToBits, Expr, HdlPartialEq, HdlPartialEqImpl, ReduceBits, ToExpr, ValueType, Valueless, CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, StructuralEq}, ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq},
}, },
int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType}, int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType},
intern::{Intern, Interned, LazyInterned}, intern::{Intern, Interned, LazyInterned},
module::transform::visit::{Fold, Folder, Visit, Visitor}, module::transform::visit::{Fold, Folder, Visit, Visitor},
sim::value::SimValue, sim::value::{SimValue, SimValuePartialEq},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter, CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter,
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties, TypeWithDeref, OpaqueSimValueWritten, StaticType, Type, TypeProperties, TypeWithDeref,
serde_impls::SerdeCanonicalType, serde_impls::SerdeCanonicalType,
}, },
util::ConstUsize, util::ConstUsize,
}; };
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
use std::{borrow::Cow, fmt, iter::FusedIterator, ops::Index}; use std::{iter::FusedIterator, ops::Index};
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> { pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
@ -28,8 +28,8 @@ pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
type_properties: TypeProperties, type_properties: TypeProperties,
} }
impl<T: Type, Len: Size> fmt::Debug for ArrayType<T, Len> { impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Array<{:?}, {}>", self.element, self.len()) write!(f, "Array<{:?}, {}>", self.element, self.len())
} }
} }
@ -109,42 +109,14 @@ impl<T: StaticType, Len: KnownSize> Default for ArrayType<T, Len> {
} }
} }
struct MakeType<T: StaticType>(Interned<T>);
impl<T: StaticType> From<MakeType<T>> for Interned<T> {
fn from(value: MakeType<T>) -> Self {
value.0
}
}
impl<T: StaticType> Default for MakeType<T> {
fn default() -> Self {
Self(T::TYPE.intern_sized())
}
}
struct MakeMaskType<T: StaticType>(Interned<T::MaskType>);
impl<T: StaticType> From<MakeMaskType<T>> for Interned<T::MaskType> {
fn from(value: MakeMaskType<T>) -> Self {
value.0
}
}
impl<T: StaticType> Default for MakeMaskType<T> {
fn default() -> Self {
Self(T::MASK_TYPE.intern_sized())
}
}
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> { impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
const TYPE: Self = Self { const TYPE: Self = Self {
element: LazyInterned::new_const::<MakeType<T>>(), element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
len: Len::SIZE, len: Len::SIZE,
type_properties: Self::TYPE_PROPERTIES, type_properties: Self::TYPE_PROPERTIES,
}; };
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> { const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
element: LazyInterned::new_const::<MakeMaskType<T>>(), element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
len: Len::SIZE, len: Len::SIZE,
type_properties: Self::MASK_TYPE_PROPERTIES, type_properties: Self::MASK_TYPE_PROPERTIES,
}; };
@ -182,15 +154,6 @@ impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
} }
} }
impl<T: Type, Len: Size> SimValueDebug for ArrayType<T, Len> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: Type, Len: Size> Type for ArrayType<T, Len> { impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
type BaseType = Array; type BaseType = Array;
type MaskType = ArrayType<T::MaskType, Len>; type MaskType = ArrayType<T::MaskType, Len>;
@ -258,7 +221,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
let value = AsMut::<[SimValue<T>]>::as_mut(value); let value = AsMut::<[SimValue<T>]>::as_mut(value);
assert_eq!(self.len(), value.len()); assert_eq!(self.len(), value.len());
for element_value in value { for element_value in value {
assert_eq!(element_value.ty(), element_ty); assert_eq!(SimValue::ty(element_value), element_ty);
let (element_opaque, rest) = opaque.split_at(element_size); let (element_opaque, rest) = opaque.split_at(element_size);
SimValue::opaque_mut(element_value).clone_from_slice(element_opaque); SimValue::opaque_mut(element_value).clone_from_slice(element_opaque);
opaque = rest; opaque = rest;
@ -275,7 +238,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
let value = AsRef::<[SimValue<T>]>::as_ref(value); let value = AsRef::<[SimValue<T>]>::as_ref(value);
assert_eq!(self.len(), value.len()); assert_eq!(self.len(), value.len());
for element_value in value { for element_value in value {
assert_eq!(element_value.ty(), element_ty); assert_eq!(SimValue::ty(element_value), element_ty);
writer.fill_prefix_with(element_size, |writer| { writer.fill_prefix_with(element_size, |writer| {
writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice()) writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice())
}); });
@ -363,37 +326,14 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
} }
} }
impl<Lhs: Type, Rhs: Type, Len: Size> HdlPartialEqImpl<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len> impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
where where
Lhs: HdlPartialEqImpl<Rhs>, Lhs: ExprPartialEq<Rhs>,
{ {
const TRY_STRUCTURAL_EQ: bool = <Lhs as HdlPartialEqImpl<Rhs>>::TRY_STRUCTURAL_EQ; fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
let lhs_ty = Expr::ty(lhs);
fn cmp_value_eq( let rhs_ty = Expr::ty(rhs);
lhs: Self, assert_eq!(lhs_ty.len(), rhs_ty.len());
lhs_value: Cow<'_, Self::SimValue>,
rhs: ArrayType<Rhs, Len>,
rhs_value: Cow<'_, <ArrayType<Rhs, Len> 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<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
assert_eq!(lhs.ty().len(), rhs.ty().len());
if Self::TRY_STRUCTURAL_EQ {
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
return retval.to_expr();
}
}
lhs.into_iter() lhs.into_iter()
.zip(rhs) .zip(rhs)
.map(|(l, r)| l.cmp_eq(r)) .map(|(l, r)| l.cmp_eq(r))
@ -401,13 +341,11 @@ where
.cast_to_bits() .cast_to_bits()
.all_one_bits() .all_one_bits()
} }
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
assert_eq!(lhs.ty().len(), rhs.ty().len()); fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
if Self::TRY_STRUCTURAL_EQ { let lhs_ty = Expr::ty(lhs);
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) { let rhs_ty = Expr::ty(rhs);
return !retval.to_expr(); assert_eq!(lhs_ty.len(), rhs_ty.len());
}
}
lhs.into_iter() lhs.into_iter()
.zip(rhs) .zip(rhs)
.map(|(l, r)| l.cmp_ne(r)) .map(|(l, r)| l.cmp_ne(r))
@ -415,19 +353,17 @@ where
.cast_to_bits() .cast_to_bits()
.any_one_bits() .any_one_bits()
} }
fn cmp_valueless_eq( }
lhs: Valueless<Self>,
rhs: Valueless<ArrayType<Rhs, Len>>, impl<Lhs: Type, Rhs: Type, Len: Size> SimValuePartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
) -> Valueless<Bool> { where
assert_eq!(lhs.ty().len(), rhs.ty().len()); Lhs: SimValuePartialEq<Rhs>,
Valueless::new(Bool) {
} fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<ArrayType<Rhs, Len>>) -> bool {
fn cmp_valueless_ne( AsRef::<[_]>::as_ref(&**this)
lhs: Valueless<Self>, .iter()
rhs: Valueless<ArrayType<Rhs, Len>>, .zip(AsRef::<[_]>::as_ref(&**other))
) -> Valueless<Bool> { .all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r))
assert_eq!(lhs.ty().len(), rhs.ty().len());
Valueless::new(Bool)
} }
} }
@ -438,7 +374,7 @@ impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> {
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter { fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter {
ExprArrayIter { ExprArrayIter {
base: e, base: e,
indexes: 0..e.ty().len(), indexes: 0..Expr::ty(e).len(),
} }
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,128 +0,0 @@
// 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<Path> {
self.base.file_with_ext("fir")
}
}
impl JobKind for FirrtlJobKind {
type Args = FirrtlArgs;
type Job = Firrtl;
type Dependencies = JobKindAndDependencies<BaseJobKind>;
fn dependencies(self) -> Self::Dependencies {
JobKindAndDependencies::new(BaseJobKind)
}
fn args_to_jobs(
args: JobArgsAndDependencies<Self>,
params: &JobParams,
global_params: &GlobalParams,
) -> eyre::Result<JobAndDependencies<Self>> {
args.args_to_jobs_simple(
params,
global_params,
|_kind, FirrtlArgs { export_options }, dependencies| {
Ok(Firrtl {
base: dependencies.get_job::<BaseJob, _>().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<str> {
"firrtl".intern()
}
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
None
}
fn run(
self,
job: &Self::Job,
inputs: &[JobItem],
params: &JobParams,
_global_params: &GlobalParams,
_acquired_job: &mut AcquiredJob,
) -> eyre::Result<Vec<JobItem>> {
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<Item = DynJobKind> {
[DynJobKind::new(FirrtlJobKind)]
}

View file

@ -1,388 +0,0 @@
// 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<OsString>,
#[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<OsString>,
}
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<OsStr>]>,
formal_mode: FormalMode,
formal_depth: u64,
formal_solver: Interned<str>,
smtbmc_extra_args: Interned<[Interned<OsStr>]>,
sby_file: Interned<Path>,
output_dir: Interned<Path>,
main_verilog_file: Interned<Path>,
}
impl WriteSbyFileJob {
pub fn sby_extra_args(&self) -> Interned<[Interned<OsStr>]> {
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<str> {
self.formal_solver
}
pub fn smtbmc_extra_args(&self) -> Interned<[Interned<OsStr>]> {
self.smtbmc_extra_args
}
pub fn sby_file(&self) -> Interned<Path> {
self.sby_file
}
pub fn output_dir(&self) -> Interned<Path> {
self.output_dir
}
pub fn main_verilog_file(&self) -> Interned<Path> {
self.main_verilog_file
}
fn write_sby(
&self,
output: &mut OsString,
additional_files: &[Interned<Path>],
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<VerilogJobKind>;
fn dependencies(self) -> Self::Dependencies {
Default::default()
}
fn args_to_jobs(
mut args: JobArgsAndDependencies<Self>,
params: &JobParams,
global_params: &GlobalParams,
) -> eyre::Result<JobAndDependencies<Self>> {
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::<BaseJob, _>();
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::<VerilogJob, _>().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<str> {
"write-sby-file".intern()
}
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
None
}
fn run(
self,
job: &Self::Job,
inputs: &[JobItem],
params: &JobParams,
_global_params: &GlobalParams,
_acquired_job: &mut AcquiredJob,
) -> eyre::Result<Vec<JobItem>> {
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<OsStr>,
}
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<str> {
"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<<UnadjustedVerilog as ExternalCommand>::BaseJobPosition>,
>,
>;
type Dependencies = JobKindAndDependencies<WriteSbyFileJobKind>;
type ExternalProgram = Symbiyosys;
fn dependencies() -> Self::Dependencies {
Default::default()
}
fn args_to_jobs(
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
params: &JobParams,
global_params: &GlobalParams,
) -> eyre::Result<(
Self::AdditionalJobData,
<Self::Dependencies as JobDependencies>::JobsAndKinds,
)> {
args.args_to_jobs_external_simple(params, global_params, |args, dependencies| {
let FormalAdditionalArgs {} = args.additional_args;
let write_sby_file = dependencies.get_job::<WriteSbyFileJob, _>().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<Self>) -> 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<Self>) -> Interned<[Interned<Path>]> {
Interned::default()
}
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, 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<Self>) -> Option<Interned<Path>> {
Some(job.output_dir())
}
fn job_kind_name() -> Interned<str> {
"formal".intern()
}
}
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
[
DynJobKind::new(WriteSbyFileJobKind),
DynJobKind::new(ExternalCommandJobKind::<Formal>::new()),
]
}

View file

@ -1,855 +0,0 @@
// 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, NodeIndex},
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<DynJob>,
},
}
type JobGraphInner = DiGraph<JobGraphNode, ()>;
#[derive(Clone, Default)]
pub struct JobGraph {
jobs: HashMap<DynJob, <JobGraphInner as GraphBase>::NodeId>,
items: HashMap<JobItemName, <JobGraphInner as GraphBase>::NodeId>,
graph: JobGraphInner,
topological_order: Vec<<JobGraphInner as GraphBase>::NodeId>,
space: DfsSpace<<JobGraphInner as GraphBase>::NodeId, <JobGraphInner as Visitable>::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<OsStr>)) -> 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<Self::Item> {
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<S: ?Sized, E>(
&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<OsStr>),
kind: UnixMakefileEscapeKind,
needed_variables: &mut BTreeSet<&'static str>,
) -> Result<Self, Utf8Error> {
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<DynJob>,
new_nodes: &mut HashSet<<JobGraphInner as GraphBase>::NodeId>,
) -> Result<<JobGraphInner as GraphBase>::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<I: IntoIterator<Item = DynJob>>(
&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<<JobGraphInner as GraphBase>::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::<NodeIndex>::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<I: IntoIterator<Item = DynJob>>(&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<OsStr>],
) -> Result<String, Utf8Error> {
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<OsStr>],
platform: Option<&DynPlatform>,
extra_args: &[Interned<OsStr>],
) -> Result<String, Utf8Error> {
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<OsStr>],
) -> 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<OsStr>],
platform: Option<&DynPlatform>,
extra_args: &[Interned<OsStr>],
) -> 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: <JobGraphInner as GraphBase>::NodeId,
job: DynJob,
inputs: BTreeMap<JobItemName, OnceCell<JobItem>>,
}
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<Vec<JobItem>>>,
}
let mut running_jobs = HashMap::<NodeIndex, RunningJob>::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: <JobGraphInner as GraphBase>::NodeId,
job: DynJob,
inputs: Vec<JobItem>,
params: &'a JobParams,
global_params: &'a GlobalParams,
acquired_job: AcquiredJob,
finished_jobs_sender: mpsc::Sender<<JobGraphInner as GraphBase>::NodeId>,
}
impl RunningJobInThread<'_> {
fn run(mut self) -> eyre::Result<Vec<JobItem>> {
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<DynJob> for JobGraph {
#[track_caller]
fn extend<T: IntoIterator<Item = DynJob>>(&mut self, iter: T) {
self.add_jobs(iter);
}
}
impl FromIterator<DynJob> for JobGraph {
#[track_caller]
fn from_iter<T: IntoIterator<Item = DynJob>>(iter: T) -> Self {
let mut retval = Self::new();
retval.add_jobs(iter);
retval
}
}
impl Serialize for JobGraph {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let jobs = Vec::<DynJob>::deserialize(deserializer)?;
let mut retval = JobGraph::new();
retval.try_add_jobs(jobs).map_err(D::Error::custom)?;
Ok(retval)
}
}

View file

@ -1,313 +0,0 @@
// 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<InternedStrCompareAsStr, DynJobKind>,
}
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<Arc<JobKindRegistry>> {
type Locked = RwLockWriteGuard<'static, Arc<JobKindRegistry>>;
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<Arc<Self>> {
static REGISTRY: OnceLock<RwLock<Arc<JobKindRegistry>>> = OnceLock::new();
REGISTRY.get_or_init(Default::default)
}
fn try_register<L: JobKindRegistryRegisterLock>(
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<L: JobKindRegistryRegisterLock>(lock: L, job_kind: DynJobKind) {
match Self::try_register(lock, job_kind) {
Err(e) => panic!("{e}"),
Ok(()) => {}
}
}
fn get() -> Arc<Self> {
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<JobKindRegistry>);
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::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn count(self) -> usize
where
Self: Sized,
{
self.0.count()
}
fn last(self) -> Option<Self::Item> {
self.0.last()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth(n)
}
fn fold<B, F>(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::Item> {
self.0.next_back()
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth_back(n)
}
fn rfold<B, F>(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<str>, &'a DynJobKind);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(name, job_kind)| (name.0, job_kind))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn count(self) -> usize
where
Self: Sized,
{
self.0.count()
}
fn last(self) -> Option<Self::Item> {
self.0.last().map(|(name, job_kind)| (name.0, job_kind))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth(n).map(|(name, job_kind)| (name.0, job_kind))
}
fn fold<B, F>(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::Item> {
self.0
.next_back()
.map(|(name, job_kind)| (name.0, job_kind))
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.0
.nth_back(n)
.map(|(name, job_kind)| (name.0, job_kind))
}
fn rfold<B, F>(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<K: JobKind>(kind: K) {
DynJobKind::new(kind).register();
}

View file

@ -1,418 +0,0 @@
// 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<OsString>,
/// adapt the generated Verilog for a particular toolchain
#[arg(long)]
pub verilog_dialect: Option<VerilogDialect>,
#[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<str> {
"firtool".intern()
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Deserialize, Serialize)]
pub struct UnadjustedVerilog {
firrtl_file: Interned<Path>,
firrtl_file_name: Interned<OsStr>,
unadjusted_verilog_file: Interned<Path>,
unadjusted_verilog_file_name: Interned<OsStr>,
firtool_extra_args: Interned<[Interned<OsStr>]>,
verilog_dialect: Option<VerilogDialect>,
verilog_debug: bool,
}
impl UnadjustedVerilog {
pub fn firrtl_file(&self) -> Interned<Path> {
self.firrtl_file
}
pub fn unadjusted_verilog_file(&self) -> Interned<Path> {
self.unadjusted_verilog_file
}
pub fn firtool_extra_args(&self) -> Interned<[Interned<OsStr>]> {
self.firtool_extra_args
}
pub fn verilog_dialect(&self) -> Option<VerilogDialect> {
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<GetJobPositionJob>;
type Dependencies = JobKindAndDependencies<FirrtlJobKind>;
type ExternalProgram = Firtool;
fn dependencies() -> Self::Dependencies {
Default::default()
}
fn args_to_jobs(
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
params: &JobParams,
global_params: &GlobalParams,
) -> eyre::Result<(
Self::AdditionalJobData,
<Self::Dependencies as JobDependencies>::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::<Firrtl, _>();
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<Self>) -> Interned<[JobItemName]> {
[JobItemName::Path {
path: job.additional_job_data().firrtl_file,
}]
.intern_slice()
}
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
[job.additional_job_data().unadjusted_verilog_file].intern_slice()
}
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, 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=named"]);
}
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<Self>) -> Option<Interned<Path>> {
Some(job.output_dir())
}
fn job_kind_name() -> Interned<str> {
"unadjusted-verilog".intern()
}
fn subcommand_hidden() -> bool {
true
}
fn run_even_if_cached_arg_name() -> Interned<str> {
"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<Path>,
unadjusted_verilog_file: Interned<Path>,
main_verilog_file: Interned<Path>,
}
impl VerilogJob {
pub fn output_dir(&self) -> Interned<Path> {
self.output_dir
}
pub fn unadjusted_verilog_file(&self) -> Interned<Path> {
self.unadjusted_verilog_file
}
pub fn main_verilog_file(&self) -> Interned<Path> {
self.main_verilog_file
}
#[track_caller]
pub fn unwrap_additional_files(additional_files: &JobItem) -> &[Interned<Path>] {
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<Path>,
additional_files: &[Interned<Path>],
) -> eyre::Result<Interned<[Interned<Path>]>> {
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<ExternalCommandJobKind<UnadjustedVerilog>>;
fn dependencies(self) -> Self::Dependencies {
Default::default()
}
fn args_to_jobs(
args: JobArgsAndDependencies<Self>,
params: &JobParams,
global_params: &GlobalParams,
) -> eyre::Result<JobAndDependencies<Self>> {
args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| {
let VerilogJobArgs {} = args;
let base_job = dependencies.get_job::<BaseJob, _>();
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<str> {
"verilog".intern()
}
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
None
}
fn run(
self,
job: &Self::Job,
inputs: &[JobItem],
_params: &JobParams,
_global_params: &GlobalParams,
_acquired_job: &mut AcquiredJob,
) -> eyre::Result<Vec<JobItem>> {
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<Item = DynJobKind> {
[
DynJobKind::new(ExternalCommandJobKind::<UnadjustedVerilog>::new()),
DynJobKind::new(VerilogJobKind),
]
}

View file

@ -3,24 +3,22 @@
use crate::{ use crate::{
expr::{ expr::{
CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType, CastToBits, Expr, ReduceBits, ToExpr,
Valueless, ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
ops::{ArrayLiteral, BundleLiteral, StructuralEq},
value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue},
}, },
int::{Bool, DynSize}, int::{Bool, DynSize},
intern::{Intern, InternSlice, Interned}, intern::{Intern, Interned},
sim::value::{SimValue, SimValueEq, ToSimValue, ToSimValueWithType}, sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize, CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize,
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type,
StaticType, Type, TypeProperties, TypeWithDeref, impl_match_variant_as_self, TypeProperties, TypeWithDeref, impl_match_variant_as_self,
}, },
util::HashMap, util::HashMap,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{borrow::Cow, fmt, marker::PhantomData}; use std::{fmt, marker::PhantomData};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleField { pub struct BundleField {
@ -271,17 +269,9 @@ impl Type for Bundle {
} }
} }
impl SimValueDebug for Bundle {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
pub trait BundleType: Type<BaseType = Bundle> { pub trait BundleType: Type<BaseType = Bundle> {
type Builder: Default; type Builder: Default;
type FilledBuilder: ToExpr<Type = Self>;
fn fields(&self) -> Interned<[BundleField]>; fn fields(&self) -> Interned<[BundleField]>;
} }
@ -328,7 +318,7 @@ impl<'a> BundleSimValueFromOpaque<'a> {
#[track_caller] #[track_caller]
pub fn field_clone_from_opaque<T: Type>(&mut self, field_value: &mut SimValue<T>) { pub fn field_clone_from_opaque<T: Type>(&mut self, field_value: &mut SimValue<T>) {
let (field_ty, field_opaque) = self.field_ty_and_opaque::<T>(); let (field_ty, field_opaque) = self.field_ty_and_opaque::<T>();
assert_eq!(field_ty, field_value.ty()); assert_eq!(field_ty, SimValue::ty(field_value));
SimValue::opaque_mut(field_value).clone_from_slice(field_opaque); SimValue::opaque_mut(field_value).clone_from_slice(field_opaque);
} }
} }
@ -364,7 +354,7 @@ impl<'a> BundleSimValueToOpaque<'a> {
else { else {
panic!("tried to write too many fields with BundleSimValueToOpaque"); panic!("tried to write too many fields with BundleSimValueToOpaque");
}; };
assert_eq!(T::from_canonical(ty), field_value.ty()); assert_eq!(T::from_canonical(ty), SimValue::ty(field_value));
self.writer.fill_prefix_with(ty.size(), |writer| { self.writer.fill_prefix_with(ty.size(), |writer| {
writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice()) writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice())
}); });
@ -384,8 +374,17 @@ impl<'a> BundleSimValueToOpaque<'a> {
#[derive(Default)] #[derive(Default)]
pub struct NoBuilder; pub struct NoBuilder;
pub struct Unfilled<T: Type>(PhantomData<T>);
impl<T: Type> Default for Unfilled<T> {
fn default() -> Self {
Self(PhantomData)
}
}
impl BundleType for Bundle { impl BundleType for Bundle {
type Builder = NoBuilder; type Builder = NoBuilder;
type FilledBuilder = Expr<Bundle>;
fn fields(&self) -> Interned<[BundleField]> { fn fields(&self) -> Interned<[BundleField]> {
self.0.fields self.0.fields
} }
@ -421,14 +420,15 @@ macro_rules! impl_tuple_builder_fields {
) => { ) => {
impl< impl<
$($head_type_var,)* $($head_type_var,)*
$cur_type_var: Type,
$($tail_type_var,)* $($tail_type_var,)*
> TupleBuilder<( > TupleBuilder<(
$($head_type_var,)* $($head_type_var,)*
(), Unfilled<$cur_type_var>,
$($tail_type_var,)* $($tail_type_var,)*
)> )>
{ {
pub fn $cur_field<$cur_type_var: Type>(self, $cur_var: impl ToExpr<Type = $cur_type_var>) -> TupleBuilder<( pub fn $cur_field(self, $cur_var: impl ToExpr<Type = $cur_type_var>) -> TupleBuilder<(
$($head_type_var,)* $($head_type_var,)*
Expr<$cur_type_var>, Expr<$cur_type_var>,
$($tail_type_var,)* $($tail_type_var,)*
@ -452,12 +452,6 @@ macro_rules! impl_tuple_builder_fields {
($global:tt []) => {}; ($global:tt []) => {};
} }
macro_rules! get_unit_ty {
($($tt:tt)*) => {
()
};
}
macro_rules! impl_tuples { macro_rules! impl_tuples {
( (
[$({ [$({
@ -480,14 +474,6 @@ macro_rules! impl_tuples {
#[var($var)] #[var($var)]
})*] })*]
} }
impl<$($T: Type,)*> SimValueDebug for ($($T,)*) {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<$($T: Type,)*> Type for ($($T,)*) { impl<$($T: Type,)*> Type for ($($T,)*) {
type BaseType = Bundle; type BaseType = Bundle;
type MaskType = ($($T::MaskType,)*); type MaskType = ($($T::MaskType,)*);
@ -559,10 +545,11 @@ macro_rules! impl_tuples {
} }
} }
impl<$($T: Type,)*> BundleType for ($($T,)*) { impl<$($T: Type,)*> BundleType for ($($T,)*) {
type Builder = TupleBuilder<($(get_unit_ty!($T),)*)>; type Builder = TupleBuilder<($(Unfilled<$T>,)*)>;
type FilledBuilder = TupleBuilder<($(Expr<$T>,)*)>;
fn fields(&self) -> Interned<[BundleField]> { fn fields(&self) -> Interned<[BundleField]> {
let ($($var,)*) = self; let ($($var,)*) = self;
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*].intern_slice() [$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern()
} }
} }
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) { impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
@ -585,56 +572,25 @@ macro_rules! impl_tuples {
builder.finish() builder.finish()
}; };
} }
impl<'a, $($T: ToSimValue,)*> ToSimValueInner<'a> for ($($T,)*) impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
where
Self: ValueType<Type = ($($T::Type,)*)>,
{
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
let ($($var,)*) = this;
Cow::Owned(($($var.to_sim_value(),)*))
}
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::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 Type = ($($T::Type,)*);
type ValueCategory = <ValueCategoryValue as ValueCategoryCommon<($($T::ValueCategory,)*)>>::Common;
fn ty(&self) -> Self::Type {
let ($($var,)*) = self;
($($var.ty(),)*)
}
}
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*)
where
Self: ValueType<Type = ($($T::Type,)*)>,
{
fn to_expr(&self) -> Expr<Self::Type> { fn to_expr(&self) -> Expr<Self::Type> {
let ($($var,)*) = self; let ($($var,)*) = self;
$(let $var = $var.to_expr();)* $(let $var = $var.to_expr();)*
let ty = ($($var.ty(),)*); let ty = ($(Expr::ty($var),)*);
let field_values = [$(Expr::canonical($var)),*]; let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, field_values.intern_slice()).to_expr() BundleLiteral::new(ty, field_values[..].intern()).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>,)*)> { impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
type Type = ($($T,)*);
fn to_expr(&self) -> Expr<Self::Type> { fn to_expr(&self) -> Expr<Self::Type> {
let ($($var,)*) = self.0; let ($($var,)*) = self.0;
let ty = ($($var.ty(),)*); let ty = ($(Expr::ty($var),)*);
let field_values = [$(Expr::canonical($var)),*]; let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, field_values.intern_slice()).to_expr() BundleLiteral::new(ty, field_values[..].intern()).to_expr()
} }
} }
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) { impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) {
@ -668,7 +624,7 @@ macro_rules! impl_tuples {
}; };
let mut opaque = OpaqueSimValue::empty(); let mut opaque = OpaqueSimValue::empty();
$(let $var = $var.into_sim_value_with_type($ty_var.ty); $(let $var = $var.into_sim_value_with_type($ty_var.ty);
assert_eq!($var.ty(), $ty_var.ty); assert_eq!(SimValue::ty(&$var), $ty_var.ty);
opaque.extend_from_slice(SimValue::opaque(&$var).as_slice()); opaque.extend_from_slice(SimValue::opaque(&$var).as_slice());
)* )*
SimValue::from_opaque(ty, opaque) SimValue::from_opaque(ty, opaque)
@ -690,94 +646,53 @@ macro_rules! impl_tuples {
SimValue::from_value(ty, ($($var,)*)) SimValue::from_value(ty, ($($var,)*))
} }
} }
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) {
where type Type = ($($T::Type,)*);
Self: ValueType<Type = ($($T::Type,)*)>,
{
#[track_caller] #[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
let ($($var,)*) = self; let ($($var,)*) = self;
$(let $var = $var.to_sim_value();)* $(let $var = $var.to_sim_value();)*
SimValue::from_value(($($var.ty(),)*), ($($var,)*)) SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
} }
#[track_caller] #[track_caller]
fn into_sim_value(self) -> SimValue<Self::Type> { fn into_sim_value(self) -> SimValue<Self::Type> {
let ($($var,)*) = self; let ($($var,)*) = self;
$(let $var = $var.to_sim_value();)* $(let $var = $var.to_sim_value();)*
SimValue::from_value(($($var.ty(),)*), ($($var,)*)) SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
}
}
impl<$($Lhs: Type + HdlPartialEqImpl<$Rhs>, $Rhs: Type,)*> HdlPartialEqImpl<($($Rhs,)*)> for ($($Lhs,)*) {
const TRY_STRUCTURAL_EQ: bool = true $(&& <$Lhs as HdlPartialEqImpl<$Rhs>>::TRY_STRUCTURAL_EQ)*;
#[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<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
if Self::TRY_STRUCTURAL_EQ {
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
return retval.to_expr();
} }
} }
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
let ($($lhs_var,)*) = *lhs; let ($($lhs_var,)*) = *lhs;
let ($($rhs_var,)*) = *rhs; let ($($rhs_var,)*) = *rhs;
ArrayLiteral::<Bool, DynSize>::new( ArrayLiteral::<Bool, DynSize>::new(
Bool, Bool,
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_eq($lhs_var, $rhs_var)),)*]), FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
) )
.cast_to_bits() .cast_to_bits()
.all_one_bits() .all_one_bits()
} }
#[track_caller] fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
if Self::TRY_STRUCTURAL_EQ {
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
return !retval.to_expr();
}
}
let ($($lhs_var,)*) = *lhs; let ($($lhs_var,)*) = *lhs;
let ($($rhs_var,)*) = *rhs; let ($($rhs_var,)*) = *rhs;
ArrayLiteral::<Bool, DynSize>::new( ArrayLiteral::<Bool, DynSize>::new(
Bool, Bool,
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_ne($lhs_var, $rhs_var)),)*]), FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
) )
.cast_to_bits() .cast_to_bits()
.any_one_bits() .any_one_bits()
} }
#[track_caller]
fn cmp_valueless_eq(lhs: Valueless<Self>, rhs: Valueless<($($Rhs,)*)>) -> Valueless<Bool> {
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)
} }
impl<$($Lhs: SimValuePartialEq<$Rhs>, $Rhs: Type,)*> SimValuePartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
#[track_caller] fn sim_value_eq(lhs: &SimValue<Self>, rhs: &SimValue<($($Rhs,)*)>) -> bool {
fn cmp_valueless_ne(lhs: Valueless<Self>, rhs: Valueless<($($Rhs,)*)>) -> Valueless<Bool> { let ($($lhs_var,)*) = &**lhs;
let ($($lhs_var,)*) = lhs.ty(); let ($($rhs_var,)*) = &**rhs;
let ($($rhs_var,)*) = rhs.ty(); let retval = true;
// let them check that the types can be compared $(let retval = retval && $lhs_var == $rhs_var;)*
$($Lhs::cmp_valueless_ne(Valueless::new($lhs_var), Valueless::new($rhs_var));)* retval
Valueless::new(Bool)
} }
} }
impl<$($T: SimValueEq + HdlPartialEqImpl<$T>,)*> SimValueEq for ($($T,)*) {}
}; };
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
impl_tuples!([$($lhs)*] []); impl_tuples!([$($lhs)*] []);
@ -802,15 +717,6 @@ impl_tuples! {
] ]
} }
impl<T: ?Sized + Send + Sync + 'static> SimValueDebug for PhantomData<T> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> { impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
type BaseType = Bundle; type BaseType = Bundle;
type MaskType = (); type MaskType = ();
@ -875,16 +781,9 @@ impl<T: ?Sized + Send + Sync + 'static> Default for PhantomDataBuilder<T> {
} }
} }
impl<T: ?Sized + Send + Sync + 'static> ValueType for PhantomDataBuilder<T> {
type Type = PhantomData<T>;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
PhantomData
}
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> { impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
type Type = PhantomData<T>;
fn to_expr(&self) -> Expr<Self::Type> { fn to_expr(&self) -> Expr<Self::Type> {
PhantomData.to_expr() PhantomData.to_expr()
} }
@ -892,6 +791,7 @@ impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
impl<T: ?Sized + Send + Sync + 'static> BundleType for PhantomData<T> { impl<T: ?Sized + Send + Sync + 'static> BundleType for PhantomData<T> {
type Builder = PhantomDataBuilder<T>; type Builder = PhantomDataBuilder<T>;
type FilledBuilder = PhantomDataBuilder<T>;
fn fields(&self) -> Interned<[BundleField]> { fn fields(&self) -> Interned<[BundleField]> {
Interned::default() Interned::default()
} }
@ -910,22 +810,17 @@ impl<T: ?Sized + Send + Sync + 'static> StaticType for PhantomData<T> {
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
} }
impl<T: ?Sized + Send + Sync + 'static> ValueType for PhantomData<T> {
type Type = PhantomData<T>;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
PhantomData
}
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> { impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
type Type = PhantomData<T>;
fn to_expr(&self) -> Expr<Self::Type> { fn to_expr(&self) -> Expr<Self::Type> {
BundleLiteral::new(PhantomData, Interned::default()).to_expr() BundleLiteral::new(PhantomData, Interned::default()).to_expr()
} }
} }
impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> { impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> {
type Type = PhantomData<T>;
#[track_caller] #[track_caller]
fn to_sim_value(&self) -> SimValue<Self> { fn to_sim_value(&self) -> SimValue<Self> {
SimValue::from_value(*self, *self) SimValue::from_value(*self, *self)

806
crates/fayalite/src/cli.rs Normal file
View file

@ -0,0 +1,806 @@
// 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<T = (), E = CliError> = std::result::Result<T, E>;
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<io::Error> for CliError {
fn from(value: io::Error) -> Self {
CliError(Report::new(value))
}
}
pub trait RunPhase<Arg> {
type Output;
fn run(&self, arg: Arg) -> Result<Self::Output> {
self.run_with_job(arg, &mut AcquiredJob::acquire())
}
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output>;
}
#[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<PathBuf>,
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
#[arg(long)]
pub file_stem: Option<String>,
#[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<TempDir>)> {
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<process::ExitStatus> {
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<TempDir>,
}
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<Bundle>,
_acquired_job: &mut AcquiredJob,
) -> Result<FirrtlOutput> {
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<T: BundleType> RunPhase<Module<T>> for FirrtlArgs {
type Output = FirrtlOutput;
fn run_with_job(
&self,
top_module: Module<T>,
acquired_job: &mut AcquiredJob,
) -> Result<Self::Output> {
self.run_impl(top_module.canonical(), acquired_job)
}
}
impl<T: BundleType> RunPhase<Interned<Module<T>>> for FirrtlArgs {
type Output = FirrtlOutput;
fn run_with_job(
&self,
top_module: Interned<Module<T>>,
acquired_job: &mut AcquiredJob,
) -> Result<Self::Output> {
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<OsString>,
/// adapt the generated Verilog for a particular toolchain
#[arg(long)]
pub verilog_dialect: Option<VerilogDialect>,
#[arg(long, short = 'g')]
pub debug: bool,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct VerilogOutput {
pub firrtl: FirrtlOutput,
pub verilog_files: Vec<PathBuf>,
pub contents_hash: Option<blake3::Hash>,
}
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<VerilogOutput> {
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<VerilogOutput> {
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<Arg> RunPhase<Arg> for VerilogArgs
where
FirrtlArgs: RunPhase<Arg, Output = FirrtlOutput>,
{
type Output = VerilogOutput;
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
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<Self, clap::Error> {
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<PathBuf> {
#[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<String>,
#[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<String>,
#[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<FormalCacheOutput, String>,
}
impl FormalCache {
pub fn new(
version: FormalCacheVersion,
contents_hash: blake3::Hash,
stdout_stderr: String,
result: Result<FormalCacheOutput, String>,
) -> Self {
Self {
version,
contents_hash,
stdout_stderr,
result,
}
}
}
impl FormalArgs {
fn sby_contents(&self, output: &FormalOutput) -> Result<String> {
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<FormalOutput> {
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<Arg> RunPhase<Arg> for FormalArgs
where
VerilogArgs: RunPhase<Arg, Output = VerilogOutput>,
{
type Output = FormalOutput;
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
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<T> RunPhase<T> for Cli
where
FirrtlArgs: RunPhase<T, Output = FirrtlOutput>,
{
type Output = ();
fn run_with_job(&self, arg: T, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
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<T>(&self, top_module: T) -> Result<()>
where
Self: RunPhase<T, Output = ()>,
{
RunPhase::run(self, top_module)
}
}

View file

@ -1,21 +1,17 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{Expr, ValueType}, expr::{Expr, ToExpr},
hdl, hdl,
int::Bool, int::Bool,
reset::{Reset, ResetType}, reset::{Reset, ResetType},
sim::value::SimValue,
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties, OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
impl_match_variant_as_self,
}, },
}; };
use bitvec::{bits, order::Lsb0}; use bitvec::{bits, order::Lsb0};
use std::fmt;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct Clock; pub struct Clock;
@ -72,15 +68,6 @@ impl Type for Clock {
} }
} }
impl SimValueDebug for Clock {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl Clock { impl Clock {
pub fn type_properties(self) -> TypeProperties { pub fn type_properties(self) -> TypeProperties {
Self::TYPE_PROPERTIES Self::TYPE_PROPERTIES
@ -104,34 +91,29 @@ impl StaticType for Clock {
} }
pub trait ToClock { pub trait ToClock {
type Output: ValueType<Type = Clock>; fn to_clock(&self) -> Expr<Clock>;
fn to_clock(&self) -> Self::Output;
} }
impl<T: ?Sized + ToClock> ToClock for &'_ T { impl<T: ?Sized + ToClock> ToClock for &'_ T {
type Output = T::Output; fn to_clock(&self) -> Expr<Clock> {
fn to_clock(&self) -> Self::Output {
(**self).to_clock() (**self).to_clock()
} }
} }
impl<T: ?Sized + ToClock> ToClock for &'_ mut T { impl<T: ?Sized + ToClock> ToClock for &'_ mut T {
type Output = T::Output; fn to_clock(&self) -> Expr<Clock> {
fn to_clock(&self) -> Self::Output {
(**self).to_clock() (**self).to_clock()
} }
} }
impl<T: ?Sized + ToClock> ToClock for Box<T> { impl<T: ?Sized + ToClock> ToClock for Box<T> {
type Output = T::Output; fn to_clock(&self) -> Expr<Clock> {
fn to_clock(&self) -> Self::Output {
(**self).to_clock() (**self).to_clock()
} }
} }
impl ToClock for Expr<Clock> { impl ToClock for Expr<Clock> {
type Output = Expr<Clock>; fn to_clock(&self) -> Expr<Clock> {
fn to_clock(&self) -> Self::Output {
*self *self
} }
} }
@ -143,25 +125,7 @@ pub struct ClockDomain<R: ResetType = Reset> {
} }
impl ToClock for bool { impl ToClock for bool {
type Output = SimValue<Clock>; fn to_clock(&self) -> Expr<Clock> {
self.to_expr().to_clock()
fn to_clock(&self) -> Self::Output {
SimValue::from_value(Clock, *self)
}
}
impl ToClock for SimValue<Bool> {
type Output = SimValue<Clock>;
fn to_clock(&self) -> Self::Output {
SimValue::from_value(Clock, **self)
}
}
impl ToClock for SimValue<Clock> {
type Output = SimValue<Clock>;
fn to_clock(&self) -> Self::Output {
self.clone()
} }
} }

View file

@ -2,7 +2,10 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{Expr, ToExpr, ValueType, ops::VariantAccess}, expr::{
Expr, ToExpr,
ops::{ExprPartialEq, VariantAccess},
},
hdl, hdl,
int::{Bool, UIntValue}, int::{Bool, UIntValue},
intern::{Intern, Interned}, intern::{Intern, Interned},
@ -10,12 +13,12 @@ use crate::{
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect, EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect,
enum_match_variants_helper, incomplete_wire, wire, enum_match_variants_helper, incomplete_wire, wire,
}, },
sim::value::{SimValue, ToSimValue, ToSimValueWithType}, sim::value::{SimValue, SimValuePartialEq},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize, CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize,
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type,
StaticType, Type, TypeProperties, TypeProperties,
}, },
util::HashMap, util::HashMap,
}; };
@ -410,15 +413,6 @@ impl Type for Enum {
} }
} }
impl SimValueDebug for Enum {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct EnumPaddingSimValue { pub struct EnumPaddingSimValue {
bits: Option<UIntValue>, bits: Option<UIntValue>,
@ -605,7 +599,7 @@ impl<'a> EnumSimValueFromOpaque<'a> {
let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else { let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else {
self.usage_error(true); self.usage_error(true);
}; };
assert_eq!(value.ty(), T::from_canonical(variant_ty)); assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
SimValue::bits_mut(value) SimValue::bits_mut(value)
.bits_mut() .bits_mut()
.copy_from_bitslice(variant_bits); .copy_from_bitslice(variant_bits);
@ -717,7 +711,7 @@ impl<'a> EnumSimValueToOpaque<'a> {
let Some(variant_ty) = self.variants[discriminant].ty else { let Some(variant_ty) = self.variants[discriminant].ty else {
panic!("expected variant to have no field"); panic!("expected variant to have no field");
}; };
assert_eq!(value.ty(), T::from_canonical(variant_ty)); assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
self.known_variant(discriminant, Some(SimValue::opaque(value)), padding) self.known_variant(discriminant, Some(SimValue::opaque(value)), padding)
} }
} }
@ -732,12 +726,85 @@ pub fn enum_type_to_sim_builder<T: EnumType>(v: T) -> T::SimBuilder {
v.into() v.into()
} }
#[hdl(cmp_eq)] #[hdl]
pub enum HdlOption<T: Type> { pub enum HdlOption<T: Type> {
HdlNone, HdlNone,
HdlSome(T), HdlSome(T),
} }
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
#[hdl]
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_eq = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
HdlNone => connect(cmp_eq, false),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_eq, false),
HdlNone => connect(cmp_eq, true),
}
}
}
cmp_eq
}
#[hdl]
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_ne = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
HdlNone => connect(cmp_ne, true),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_ne, true),
HdlNone => connect(cmp_ne, false),
}
}
}
cmp_ne
}
}
impl<Lhs: SimValuePartialEq<Rhs>, Rhs: Type> SimValuePartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<HdlOption<Rhs>>) -> bool {
type SimValueMatch<T> = <T as Type>::SimValue;
match (&**this, &**other) {
(SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_)) => {
true
}
(SimValueMatch::<Self>::HdlSome(..), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_))
| (SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlSome(..)) => {
false
}
(
SimValueMatch::<Self>::HdlSome(l, _),
SimValueMatch::<HdlOption<Rhs>>::HdlSome(r, _),
) => l == r,
}
}
}
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> { pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
HdlOption[T::TYPE].HdlNone() HdlOption[T::TYPE].HdlNone()
@ -746,124 +813,7 @@ pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> { pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
let value = value.to_expr(); let value = value.to_expr();
HdlOption[value.ty()].HdlSome(value) HdlOption[Expr::ty(value)].HdlSome(value)
}
impl<T: Type> From<SimValue<HdlOption<T>>> for Option<SimValue<T>> {
#[hdl]
fn from(value: SimValue<HdlOption<T>>) -> Self {
#[hdl(sim)]
match value {
HdlSome(v) => Some(v),
HdlNone => None,
}
}
}
impl<'a, T: Type> From<&'a SimValue<HdlOption<T>>> for Option<&'a SimValue<T>> {
#[hdl]
fn from(value: &'a SimValue<HdlOption<T>>) -> Self {
#[hdl(sim)]
match value {
HdlSome(v) => Some(v),
HdlNone => None,
}
}
}
impl<'a, T: Type> From<&'a mut SimValue<HdlOption<T>>> for Option<&'a mut SimValue<T>> {
#[hdl]
fn from(value: &'a mut SimValue<HdlOption<T>>) -> Self {
#[hdl(sim)]
match value {
HdlSome(v) => Some(v),
HdlNone => None,
}
}
}
impl<T: ValueType<Type: StaticType<MaskType: StaticType>>> ValueType for Option<T> {
type Type = HdlOption<T::Type>;
type ValueCategory = T::ValueCategory;
fn ty(&self) -> Self::Type {
StaticType::TYPE
}
}
impl<T: Type, V: ToSimValueWithType<T>> ToSimValueWithType<HdlOption<T>> for Option<V> {
#[hdl]
fn to_sim_value_with_type(&self, ty: HdlOption<T>) -> SimValue<HdlOption<T>> {
match self {
Some(v) =>
{
#[hdl(sim)]
ty.HdlSome(v)
}
None =>
{
#[hdl(sim)]
ty.HdlNone()
}
}
}
#[hdl]
fn into_sim_value_with_type(self, ty: HdlOption<T>) -> SimValue<HdlOption<T>> {
match self {
Some(v) =>
{
#[hdl(sim)]
ty.HdlSome(v)
}
None =>
{
#[hdl(sim)]
ty.HdlNone()
}
}
}
}
impl<T: ToSimValue<Type: StaticType<MaskType: StaticType>>> ToSimValue for Option<T> {
#[hdl]
fn to_sim_value(&self) -> SimValue<Self::Type> {
match self {
Some(v) =>
{
#[hdl(sim)]
HdlSome(v)
}
None =>
{
#[hdl(sim)]
HdlNone()
}
}
}
#[hdl]
fn into_sim_value(self) -> SimValue<Self::Type> {
match self {
Some(v) =>
{
#[hdl(sim)]
HdlSome(v)
}
None =>
{
#[hdl(sim)]
HdlNone()
}
}
}
}
impl<T: ToExpr<Type: StaticType<MaskType: StaticType>>> ToExpr for Option<T> {
fn to_expr(&self) -> Expr<Self::Type> {
match self {
Some(v) => HdlSome(v),
None => HdlNone(),
}
}
} }
impl<T: Type> HdlOption<T> { impl<T: Type> HdlOption<T> {
@ -903,7 +853,7 @@ impl<T: Type> HdlOption<T> {
let value = f(value).inspect_err(|_| { let value = f(value).inspect_err(|_| {
and_then_out.complete(()); // avoid error and_then_out.complete(()); // avoid error
})?; })?;
let and_then_out = and_then_out.complete(value.ty()); let and_then_out = and_then_out.complete(Expr::ty(value));
connect(and_then_out, value); connect(and_then_out, value);
drop(some_scope); drop(some_scope);
let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) = let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) =
@ -911,7 +861,7 @@ impl<T: Type> HdlOption<T> {
else { else {
unreachable!(); unreachable!();
}; };
connect(and_then_out, and_then_out.ty().HdlNone()); connect(and_then_out, Expr::ty(and_then_out).HdlNone());
drop(none_scope); drop(none_scope);
Ok(and_then_out) Ok(and_then_out)
} }
@ -929,8 +879,8 @@ impl<T: Type> HdlOption<T> {
#[track_caller] #[track_caller]
pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> { pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> {
#[hdl] #[hdl]
let and_out = wire(opt_b.ty()); let and_out = wire(Expr::ty(opt_b));
connect(and_out, opt_b.ty().HdlNone()); connect(and_out, Expr::ty(opt_b).HdlNone());
#[hdl] #[hdl]
if let HdlSome(_) = expr { if let HdlSome(_) = expr {
connect(and_out, opt_b); connect(and_out, opt_b);
@ -944,8 +894,8 @@ impl<T: Type> HdlOption<T> {
f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>, f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>,
) -> Result<Expr<Self>, E> { ) -> Result<Expr<Self>, E> {
#[hdl] #[hdl]
let filtered = wire(expr.ty()); let filtered = wire(Expr::ty(expr));
connect(filtered, expr.ty().HdlNone()); connect(filtered, Expr::ty(expr).HdlNone());
let mut f = Some(f); let mut f = Some(f);
#[hdl] #[hdl]
if let HdlSome(v) = expr { if let HdlSome(v) = expr {
@ -1019,7 +969,7 @@ impl<T: Type> HdlOption<T> {
f: impl FnOnce(Expr<T>) -> Expr<R>, f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<R> { ) -> Expr<R> {
#[hdl] #[hdl]
let mapped = wire(default.ty()); let mapped = wire(Expr::ty(default));
let mut f = Some(f); let mut f = Some(f);
#[hdl] #[hdl]
match expr { match expr {
@ -1044,12 +994,12 @@ impl<T: Type> HdlOption<T> {
match expr { match expr {
HdlSome(v) => { HdlSome(v) => {
let v = f.take().unwrap()(v); let v = f.take().unwrap()(v);
let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty())); let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
connect(mapped, v); connect(mapped, v);
} }
HdlNone => { HdlNone => {
let v = default.take().unwrap()(); let v = default.take().unwrap()();
let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty())); let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
connect(mapped, v); connect(mapped, v);
} }
} }
@ -1059,7 +1009,7 @@ impl<T: Type> HdlOption<T> {
#[track_caller] #[track_caller]
pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> { pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
#[hdl] #[hdl]
let or_out = wire(expr.ty()); let or_out = wire(Expr::ty(expr));
connect(or_out, opt_b); connect(or_out, opt_b);
#[hdl] #[hdl]
if let HdlSome(_) = expr { if let HdlSome(_) = expr {
@ -1071,7 +1021,7 @@ impl<T: Type> HdlOption<T> {
#[track_caller] #[track_caller]
pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> { pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> {
#[hdl] #[hdl]
let or_else_out = wire(expr.ty()); let or_else_out = wire(Expr::ty(expr));
connect(or_else_out, f()); connect(or_else_out, f());
#[hdl] #[hdl]
if let HdlSome(_) = expr { if let HdlSome(_) = expr {
@ -1083,7 +1033,7 @@ impl<T: Type> HdlOption<T> {
#[track_caller] #[track_caller]
pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> { pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> {
#[hdl] #[hdl]
let unwrap_or_else_out = wire(default.ty()); let unwrap_or_else_out = wire(Expr::ty(default));
connect(unwrap_or_else_out, default); connect(unwrap_or_else_out, default);
#[hdl] #[hdl]
if let HdlSome(v) = expr { if let HdlSome(v) = expr {
@ -1095,7 +1045,7 @@ impl<T: Type> HdlOption<T> {
#[track_caller] #[track_caller]
pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> { pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> {
#[hdl] #[hdl]
let unwrap_or_else_out = wire(expr.ty().HdlSome); let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome);
connect(unwrap_or_else_out, f()); connect(unwrap_or_else_out, f());
#[hdl] #[hdl]
if let HdlSome(v) = expr { if let HdlSome(v) = expr {
@ -1107,14 +1057,14 @@ impl<T: Type> HdlOption<T> {
#[track_caller] #[track_caller]
pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> { pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
#[hdl] #[hdl]
let xor_out = wire(expr.ty()); let xor_out = wire(Expr::ty(expr));
#[hdl] #[hdl]
if let HdlSome(_) = expr { if let HdlSome(_) = expr {
#[hdl] #[hdl]
if let HdlNone = opt_b { if let HdlNone = opt_b {
connect(xor_out, expr); connect(xor_out, expr);
} else { } else {
connect(xor_out, expr.ty().HdlNone()); connect(xor_out, Expr::ty(expr).HdlNone());
} }
} else { } else {
connect(xor_out, opt_b); connect(xor_out, opt_b);
@ -1125,8 +1075,8 @@ impl<T: Type> HdlOption<T> {
#[track_caller] #[track_caller]
pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> { pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> {
#[hdl] #[hdl]
let zip_out = wire(HdlOption[(expr.ty().HdlSome, other.ty().HdlSome)]); let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]);
connect(zip_out, zip_out.ty().HdlNone()); connect(zip_out, Expr::ty(zip_out).HdlNone());
#[hdl] #[hdl]
if let HdlSome(l) = expr { if let HdlSome(l) = expr {
#[hdl] #[hdl]
@ -1143,11 +1093,11 @@ impl<T: Type> HdlOption<HdlOption<T>> {
#[track_caller] #[track_caller]
pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> { pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> {
#[hdl] #[hdl]
let flattened = wire(expr.ty().HdlSome); let flattened = wire(Expr::ty(expr).HdlSome);
#[hdl] #[hdl]
match expr { match expr {
HdlSome(v) => connect(flattened, v), HdlSome(v) => connect(flattened, v),
HdlNone => connect(flattened, expr.ty().HdlSome.HdlNone()), HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()),
} }
flattened flattened
} }
@ -1157,7 +1107,7 @@ impl<T: Type, U: Type> HdlOption<(T, U)> {
#[hdl] #[hdl]
#[track_caller] #[track_caller]
pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> { pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> {
let (t, u) = expr.ty().HdlSome; let (t, u) = Expr::ty(expr).HdlSome;
#[hdl] #[hdl]
let unzipped = wire((HdlOption[t], HdlOption[u])); let unzipped = wire((HdlOption[t], HdlOption[u]));
connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone())); connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone()));

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,222 +0,0 @@
// 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 = <<Valueless<<$ty as ValueType>::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 = <<Valueless<<$ty as ValueType>::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 = <<Valueless<<$ty as ValueType>::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<L, R> NoImpl for (L, R) {
fn check_impl(&self) -> &'static dyn NoImpl {
unreachable!()
}
}
impl<L: std::ops::$Trait<R>, 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 = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::Shl<usize>>::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 = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::Shr<usize>>::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 = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::$Trait<Valueless<<$($R)* as ValueType>::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<Bool>))]
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
}
make_impls! {
#[kinds((Valueless<Bool>))]
#[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<Bool> 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<Bool>))]
#[kinds(bool)]
assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor}
}
make_impls! {
#[kinds(bool)]
#[kinds((Expr<Bool>))]
assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor}
}

View file

@ -3,15 +3,14 @@
use crate::{ use crate::{
array::Array, array::Array,
bundle::{Bundle, BundleField}, bundle::{Bundle, BundleField},
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr}, expr::{Expr, Flow, ToExpr},
formal::FormalInput,
intern::{Intern, Interned}, intern::{Intern, Interned},
memory::{DynPortType, MemPort}, memory::{DynPortType, MemPort},
module::{Instance, ModuleIO, TargetName}, module::{Instance, ModuleIO, TargetName},
reg::Reg, reg::Reg,
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, TraceAsString, Type}, ty::{CanonicalType, Type},
wire::Wire, wire::Wire,
}; };
use std::fmt; use std::fmt;
@ -47,33 +46,11 @@ impl fmt::Display for TargetPathDynArrayElement {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathTraceAsStringInner {}
impl fmt::Display for TargetPathTraceAsStringInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, ".<inner>")
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathToTraceAsString {
pub ty: TraceAsString<CanonicalType>,
}
impl fmt::Display for TargetPathToTraceAsString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, ".to_trace_as_string()")
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TargetPathElement { pub enum TargetPathElement {
BundleField(TargetPathBundleField), BundleField(TargetPathBundleField),
ArrayElement(TargetPathArrayElement), ArrayElement(TargetPathArrayElement),
DynArrayElement(TargetPathDynArrayElement), DynArrayElement(TargetPathDynArrayElement),
TraceAsStringInner(TargetPathTraceAsStringInner),
ToTraceAsString(TargetPathToTraceAsString),
} }
impl From<TargetPathBundleField> for TargetPathElement { impl From<TargetPathBundleField> for TargetPathElement {
@ -94,26 +71,12 @@ impl From<TargetPathDynArrayElement> for TargetPathElement {
} }
} }
impl From<TargetPathTraceAsStringInner> for TargetPathElement {
fn from(value: TargetPathTraceAsStringInner) -> Self {
Self::TraceAsStringInner(value)
}
}
impl From<TargetPathToTraceAsString> for TargetPathElement {
fn from(value: TargetPathToTraceAsString) -> Self {
Self::ToTraceAsString(value)
}
}
impl fmt::Display for TargetPathElement { impl fmt::Display for TargetPathElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::BundleField(v) => v.fmt(f), Self::BundleField(v) => v.fmt(f),
Self::ArrayElement(v) => v.fmt(f), Self::ArrayElement(v) => v.fmt(f),
Self::DynArrayElement(v) => v.fmt(f), Self::DynArrayElement(v) => v.fmt(f),
Self::TraceAsStringInner(v) => v.fmt(f),
Self::ToTraceAsString(v) => v.fmt(f),
} }
} }
} }
@ -137,15 +100,6 @@ impl TargetPathElement {
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty()); let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
parent_ty.element() parent_ty.element()
} }
Self::TraceAsStringInner(_) => {
let parent_ty =
TraceAsString::<CanonicalType>::from_canonical(parent.canonical_ty());
parent_ty.inner_ty()
}
&Self::ToTraceAsString(TargetPathToTraceAsString { ty }) => {
assert_eq!(parent.canonical_ty(), ty.inner_ty());
ty.canonical()
}
} }
} }
pub fn flow(&self, parent: Interned<Target>) -> Flow { pub fn flow(&self, parent: Interned<Target>) -> Flow {
@ -157,18 +111,13 @@ impl TargetPathElement {
.expect("field name is known to be a valid field of parent type"); .expect("field name is known to be a valid field of parent type");
parent.flow().flip_if(field.flipped) parent.flow().flip_if(field.flipped)
} }
Self::ArrayElement(_) Self::ArrayElement(_) => parent.flow(),
| Self::DynArrayElement(_) Self::DynArrayElement(_) => parent.flow(),
| Self::TraceAsStringInner(_)
| Self::ToTraceAsString(_) => parent.flow(),
} }
} }
pub fn is_static(&self) -> bool { pub fn is_static(&self) -> bool {
match self { match self {
Self::BundleField(_) Self::BundleField(_) | Self::ArrayElement(_) => true,
| Self::ArrayElement(_)
| Self::TraceAsStringInner(_)
| Self::ToTraceAsString(_) => true,
Self::DynArrayElement(_) => false, Self::DynArrayElement(_) => false,
} }
} }
@ -247,18 +196,9 @@ macro_rules! impl_target_base {
} }
} }
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 { impl ToExpr for $TargetBase {
type Type = CanonicalType;
fn to_expr(&self) -> Expr<Self::Type> { fn to_expr(&self) -> Expr<Self::Type> {
match self { match self {
$(Self::$Variant(v) => Expr::canonical(v.to_expr()),)* $(Self::$Variant(v) => Expr::canonical(v.to_expr()),)*
@ -296,14 +236,6 @@ impl_target_base! {
#[is = is_instance] #[is = is_instance]
#[to = instance] #[to = instance]
Instance(Instance<Bundle>), Instance(Instance<Bundle>),
#[from = from]
#[is = is_formal_input]
#[to = formal_input]
FormalInput(FormalInput),
#[from = from]
#[is = is_sim_io_for_global]
#[to = sim_io_for_global]
SimIoForGlobal(crate::expr::ops::SimIoForGlobal),
} }
} }
@ -352,8 +284,6 @@ impl TargetBase {
TargetBase::RegAsync(v) => TargetName(v.scoped_name(), None), TargetBase::RegAsync(v) => TargetName(v.scoped_name(), None),
TargetBase::Wire(v) => TargetName(v.scoped_name(), None), TargetBase::Wire(v) => TargetName(v.scoped_name(), None),
TargetBase::Instance(v) => TargetName(v.scoped_name(), None), TargetBase::Instance(v) => TargetName(v.scoped_name(), None),
TargetBase::FormalInput(v) => TargetName(v.scoped_name(), None),
TargetBase::SimIoForGlobal(v) => TargetName(v.global().scoped_name(), None),
} }
} }
pub fn canonical_ty(&self) -> CanonicalType { pub fn canonical_ty(&self) -> CanonicalType {
@ -365,21 +295,6 @@ impl TargetBase {
TargetBase::RegAsync(v) => v.ty(), TargetBase::RegAsync(v) => v.ty(),
TargetBase::Wire(v) => v.ty(), TargetBase::Wire(v) => v.ty(),
TargetBase::Instance(v) => v.ty().canonical(), TargetBase::Instance(v) => v.ty().canonical(),
TargetBase::FormalInput(v) => v.ty(),
TargetBase::SimIoForGlobal(v) => v.ty(),
}
}
pub fn is_valid_annotation_target(&self) -> bool {
match self {
Self::ModuleIO(_) => true,
Self::MemPort(_) => true,
Self::Reg(_) => true,
Self::RegSync(_) => true,
Self::RegAsync(_) => true,
Self::Wire(_) => true,
Self::Instance(_) => true,
Self::FormalInput(_) => false,
Self::SimIoForGlobal(_) => false,
} }
} }
} }
@ -390,7 +305,6 @@ pub struct TargetChild {
path_element: Interned<TargetPathElement>, path_element: Interned<TargetPathElement>,
canonical_ty: CanonicalType, canonical_ty: CanonicalType,
flow: Flow, flow: Flow,
canonicalized_if_different: Option<Interned<Target>>,
} }
impl fmt::Debug for TargetChild { impl fmt::Debug for TargetChild {
@ -400,7 +314,6 @@ impl fmt::Debug for TargetChild {
path_element, path_element,
canonical_ty: _, canonical_ty: _,
flow: _, flow: _,
canonicalized_if_different: _,
} = self; } = self;
parent.fmt(f)?; parent.fmt(f)?;
fmt::Display::fmt(path_element, f) fmt::Display::fmt(path_element, f)
@ -414,7 +327,6 @@ impl fmt::Display for TargetChild {
path_element, path_element,
canonical_ty: _, canonical_ty: _,
flow: _, flow: _,
canonicalized_if_different: _,
} = self; } = self;
parent.fmt(f)?; parent.fmt(f)?;
path_element.fmt(f) path_element.fmt(f)
@ -422,69 +334,14 @@ impl fmt::Display for TargetChild {
} }
impl TargetChild { impl TargetChild {
fn new_helper( pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self {
parent: Interned<Target>,
path_element: Interned<TargetPathElement>,
canonicalized_if_different: Option<Interned<Target>>,
) -> Self {
Self { Self {
parent, parent,
path_element, path_element,
canonical_ty: path_element.canonical_ty(parent), canonical_ty: path_element.canonical_ty(parent),
flow: path_element.flow(parent), flow: path_element.flow(parent),
canonicalized_if_different,
} }
} }
fn make_canonicalized_if_different(
parent: Interned<Target>,
path_element: Interned<TargetPathElement>,
) -> Option<Interned<Target>> {
use TargetPathElement::*;
match *path_element {
BundleField(_) => {}
ArrayElement(_) => {}
DynArrayElement(_) => {}
TraceAsStringInner(_) => {
if let Some(child) = parent.canonicalized().child() {
match *child.path_element() {
BundleField(_)
| ArrayElement(_)
| DynArrayElement(_)
| TraceAsStringInner(_) => {}
ToTraceAsString(_) => return Some(child.parent()),
}
}
}
ToTraceAsString(TargetPathToTraceAsString { ty }) => {
if let Some(child) = parent.canonicalized().child() {
match *child.path_element() {
BundleField(_) | ArrayElement(_) | DynArrayElement(_)
| ToTraceAsString(_) => {}
TraceAsStringInner(_) => {
if ty.canonical() == child.parent().canonical_ty() {
return Some(child.parent());
}
}
}
}
}
}
Some(
Target::Child(Self::new_helper(
parent.canonicalized_if_different()?,
path_element,
None,
))
.intern_sized(),
)
}
pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self {
Self::new_helper(
parent,
path_element,
Self::make_canonicalized_if_different(parent, path_element),
)
}
pub fn parent(self) -> Interned<Target> { pub fn parent(self) -> Interned<Target> {
self.parent self.parent
} }
@ -497,19 +354,6 @@ impl TargetChild {
pub fn flow(self) -> Flow { pub fn flow(self) -> Flow {
self.flow self.flow
} }
pub fn is_canonicalized(self) -> bool {
self.canonicalized_if_different.is_none()
}
pub fn canonicalized_if_different(self) -> Option<Interned<Target>> {
self.canonicalized_if_different
}
#[must_use]
pub fn canonicalized(self) -> Target {
match self.canonicalized_if_different {
Some(v) => *v,
None => Target::Child(self),
}
}
pub fn bundle_field(self) -> Option<BundleField> { pub fn bundle_field(self) -> Option<BundleField> {
if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element { if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element {
let parent_ty = Bundle::from_canonical(self.parent.canonical_ty()); let parent_ty = Bundle::from_canonical(self.parent.canonical_ty());
@ -574,16 +418,6 @@ impl Target {
} }
} }
} }
pub fn is_valid_annotation_target(&self) -> bool {
let mut target = self;
loop {
match target {
Self::Base(target_base) => return target_base.is_valid_annotation_target(),
Self::Child(v) if !v.path_element().is_static() => return false,
Self::Child(v) => target = &v.parent,
}
}
}
#[must_use] #[must_use]
pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self { pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self {
TargetChild::new(self.intern(), path_element).into() TargetChild::new(self.intern(), path_element).into()
@ -600,82 +434,6 @@ impl Target {
Target::Child(v) => v.canonical_ty(), Target::Child(v) => v.canonical_ty(),
} }
} }
pub fn is_canonicalized(self) -> bool {
match self {
Self::Base(_) => true,
Self::Child(child) => child.is_canonicalized(),
}
}
pub fn canonicalized_if_different(self) -> Option<Interned<Self>> {
match self {
Self::Base(_) => None,
Self::Child(child) => child.canonicalized_if_different(),
}
}
#[must_use]
pub fn canonicalized(self) -> Target {
match self.canonicalized_if_different() {
Some(v) => *v,
None => self,
}
}
#[must_use]
pub fn canonicalized_interned(this: Interned<Target>) -> Interned<Target> {
this.canonicalized_if_different().unwrap_or(this)
}
#[must_use]
pub fn unwrap_transparent_types(mut self) -> Target {
loop {
self = self.canonicalized();
match self.canonical_ty() {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => return self,
CanonicalType::TraceAsString(_) => {
if let Self::Child(child) = self
&& let TargetPathElement::ToTraceAsString(_) = *child.path_element()
{
self = *child.parent();
} else {
self = self.join(TargetPathElement::intern_sized(
TargetPathTraceAsStringInner {}.into(),
));
}
}
}
}
}
#[must_use]
pub fn unwrap_transparent_types_interned(this: Interned<Target>) -> Interned<Target> {
let retval = this.unwrap_transparent_types();
if retval != *this {
retval.intern_sized()
} else {
this
}
}
#[must_use]
pub fn without_trailing_transparent_path_elements(mut self) -> Target {
use TargetPathElement::*;
loop {
match self {
Self::Base(_) => return self,
Self::Child(child) => match *child.path_element() {
BundleField(_) | ArrayElement(_) | DynArrayElement(_) => return self,
TraceAsStringInner(_) | ToTraceAsString(_) => self = *child.parent(),
},
}
}
}
} }
impl fmt::Display for Target { impl fmt::Display for Target {
@ -700,18 +458,6 @@ pub trait GetTarget {
fn target(&self) -> Option<Interned<Target>>; fn target(&self) -> Option<Interned<Target>>;
} }
impl GetTarget for Target {
fn target(&self) -> Option<Interned<Target>> {
Some(self.intern())
}
}
impl GetTarget for TargetBase {
fn target(&self) -> Option<Interned<Target>> {
Some(Target::Base(self.intern()).intern_sized())
}
}
impl GetTarget for bool { impl GetTarget for bool {
fn target(&self) -> Option<Interned<Target>> { fn target(&self) -> Option<Interned<Target>> {
None None

View file

@ -1,378 +0,0 @@
// 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<D: ValueCategoryDispatch<InputValueCategory = Self>>: ValueType<Type = D::Type, ValueCategory: ValueCategoryIsValueSimValueExprOrValueless>;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D>;
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D>;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
pub struct ValueCategoryValue;
impl ValueCategorySealed for ValueCategoryValue {}
impl ValueCategory for ValueCategoryValue {
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::Value;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D> {
dispatch.dispatch_value()
}
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D> {
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: ValueCategoryDispatch<InputValueCategory = Self>> = D::SimValue;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D> {
dispatch.dispatch_sim_value()
}
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D> {
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: ValueCategoryDispatch<InputValueCategory = Self>> = D::Expr;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D> {
dispatch.dispatch_expr()
}
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D> {
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: ValueCategoryDispatch<InputValueCategory = Self>> = D::Valueless;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D> {
dispatch.dispatch_valueless()
}
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D> {
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<This, $FirstT, $($T),*> ValueCategoryCommon<($FirstT, $($T),*)> for This
where
$FirstT: ValueCategory,
$($T: ValueCategory,)*
This: ValueCategoryCommon<($FirstT,), Common: ValueCategoryCommon<($($T,)*)>>,
{
type Common = <<This as ValueCategoryCommon<($FirstT,)>>::Common as ValueCategoryCommon<($($T,)*)>>::Common;
}
};
}
impl_value_category_common!(T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0);
impl<T: ValueCategory> ValueCategoryCommonSealed for (T,) {}
impl ValueCategoryCommonSealed for () {}
impl<T: ValueCategory> ValueCategoryCommon<()> for T {
type Common = T;
}
impl<T: ValueCategoryIsValue> ValueCategoryCommon<(ValueCategoryValue,)> for T {
type Common = ValueCategoryValue;
}
impl<T: ValueCategoryIsValueOrSimValue> ValueCategoryCommon<(ValueCategorySimValue,)> for T {
type Common = ValueCategorySimValue;
}
impl<T: ValueCategoryIsValue> ValueCategoryCommon<(T,)> for ValueCategorySimValue {
type Common = ValueCategorySimValue;
}
impl<T: ValueCategoryIsValueSimValueOrExpr> ValueCategoryCommon<(ValueCategoryExpr,)> for T {
type Common = ValueCategoryExpr;
}
impl<T: ValueCategoryIsValueOrSimValue> ValueCategoryCommon<(T,)> for ValueCategoryExpr {
type Common = ValueCategoryExpr;
}
impl<T: ValueCategoryIsValueSimValueExprOrValueless> ValueCategoryCommon<(ValueCategoryValueless,)>
for T
{
type Common = ValueCategoryValueless;
}
impl<T: ValueCategoryIsValueSimValueOrExpr> ValueCategoryCommon<(T,)> for ValueCategoryValueless {
type Common = ValueCategoryValueless;
}
pub trait ValueCategoryDispatch: Sized {
type Type: Type;
type InputValueCategory: ValueCategory;
type Value: ValueType<ValueCategory: ValueCategoryIsValueSimValueExprOrValueless, Type = Self::Type>;
type SimValue: ValueType<ValueCategory: ValueCategoryIsSimValueExprOrValueless, Type = Self::Type>;
type Expr: ValueType<ValueCategory: ValueCategoryIsExprOrValueless, Type = Self::Type>;
type Valueless: ValueType<ValueCategory: ValueCategoryIsValueless, Type = Self::Type>;
fn dispatch_value(self) -> Self::Value
where
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryValue>;
fn dispatch_sim_value(self) -> Self::SimValue
where
Self: ValueCategoryDispatch<InputValueCategory = ValueCategorySimValue>;
fn dispatch_expr(self) -> Self::Expr
where
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryExpr>;
fn dispatch_valueless(self) -> Self::Valueless
where
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryValueless>;
fn dispatch(self) -> <Self::InputValueCategory as ValueCategory>::DispatchOutput<Self> {
Self::InputValueCategory::dispatch(self)
}
fn dispatch_enum(self) -> ValueCategoryDispatchOutputEnum<Self> {
Self::InputValueCategory::dispatch_enum(self)
}
}
pub enum ValueCategoryDispatchOutputEnum<T: ValueCategoryDispatch> {
Value(T::Value),
SimValue(T::SimValue),
Expr(T::Expr),
Valueless(T::Valueless),
}
impl<T: ValueCategoryDispatch> ValueCategoryDispatchOutputEnum<T> {
pub fn try_into_value(self) -> Result<T::Value, Self> {
if let Self::Value(v) = self {
Ok(v)
} else {
Err(self)
}
}
pub fn try_into_sim_value(self) -> Result<T::SimValue, Self> {
if let Self::SimValue(v) = self {
Ok(v)
} else {
Err(self)
}
}
pub fn try_into_expr(self) -> Result<T::Expr, Self> {
if let Self::Expr(v) = self {
Ok(v)
} else {
Err(self)
}
}
pub fn try_into_valueless(self) -> Result<T::Valueless, Self> {
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> = 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,
);
}

File diff suppressed because it is too large Load diff

View file

@ -1,189 +1,11 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::target::{GetTarget, Target},
int::BoolOrIntType, int::BoolOrIntType,
intern::{Intern, Interned}, intern::{Intern, Interned, Memoize},
module::{NameId, NameIdOrGlobal, ScopedNameId},
prelude::*, prelude::*,
}; };
use std::{fmt, sync::OnceLock}; use std::sync::OnceLock;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum FormalInputKind {
FormalGlobalClock,
FormalReset,
AnyConst,
AnySeq,
AllConst,
AllSeq,
}
impl FormalInputKind {
pub fn fixed_ty(self) -> Option<CanonicalType> {
match self {
Self::FormalGlobalClock => Some(Clock.into()),
Self::FormalReset => Some(SyncReset.into()),
Self::AnyConst => None,
Self::AnySeq => None,
Self::AllConst => None,
Self::AllSeq => None,
}
}
pub fn fixed_id(self) -> Option<crate::module::Id> {
struct Cache {
formal_global_clock: crate::module::Id,
formal_reset: crate::module::Id,
}
static CACHE: OnceLock<Cache> = OnceLock::new();
let cache = || {
CACHE.get_or_init(
#[cold]
|| Cache {
formal_global_clock: crate::module::Id::new(),
formal_reset: crate::module::Id::new(),
},
)
};
match self {
Self::FormalGlobalClock => Some(cache().formal_global_clock),
Self::FormalReset => Some(cache().formal_reset),
Self::AnyConst => None,
Self::AnySeq => None,
Self::AllConst => None,
Self::AllSeq => None,
}
}
pub fn fixed_source_location(self) -> Option<SourceLocation> {
match self {
Self::FormalGlobalClock | Self::FormalReset => Some(SourceLocation::builtin()),
Self::AnyConst | Self::AnySeq | Self::AllConst | Self::AllSeq => None,
}
}
pub fn name(self) -> &'static str {
match self {
Self::FormalGlobalClock => "formal_global_clock",
Self::FormalReset => "formal_reset",
Self::AnyConst => "any_const",
Self::AnySeq => "any_seq",
Self::AllConst => "all_const",
Self::AllSeq => "all_seq",
}
}
pub fn interned_name(self) -> Interned<str> {
macro_rules! impl_interned_name {
($($variant:ident,)*) => {
match self {
$(Self::$variant => {
static CACHE: OnceLock<Interned<str>> = OnceLock::new();
*CACHE.get_or_init(|| Self::$variant.name().intern())
})*
}
};
}
impl_interned_name! {
FormalGlobalClock,
FormalReset,
AnyConst,
AnySeq,
AllConst,
AllSeq,
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
struct FormalInputData {
kind: FormalInputKind,
name_id: NameId,
ty: CanonicalType,
source_location: SourceLocation,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct FormalInput(Interned<FormalInputData>);
impl fmt::Debug for FormalInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.kind().fixed_ty().is_some() {
f.write_str(&self.name())
} else {
f.debug_tuple(&self.name()).field(&self.0.ty).finish()
}
}
}
impl FormalInput {
#[track_caller]
pub fn new(
kind: FormalInputKind,
name_id: NameId,
ty: CanonicalType,
source_location: SourceLocation,
) -> Self {
let NameId(name, id) = name_id;
assert_eq!(kind.interned_name(), name);
if let Some(fixed_ty) = kind.fixed_ty() {
assert_eq!(ty, fixed_ty);
} else {
assert!(
ty.is_castable_from_bits(),
"{name} type must be castable from bits. got:\n{ty:#?}",
);
}
if let Some(fixed_source_location) = kind.fixed_source_location() {
assert_eq!(source_location, fixed_source_location);
}
if let Some(fixed_id) = kind.fixed_id() {
assert_eq!(id, fixed_id);
}
Self(
FormalInputData {
kind,
name_id,
ty,
source_location,
}
.intern_sized(),
)
}
pub fn kind(self) -> FormalInputKind {
self.0.kind
}
pub fn name(self) -> Interned<str> {
self.0.name_id.0
}
pub fn name_id(self) -> NameId {
self.0.name_id
}
pub fn scoped_name(self) -> ScopedNameId {
ScopedNameId(NameIdOrGlobal::Global, self.name_id())
}
pub fn source_location(self) -> SourceLocation {
self.0.source_location
}
pub(crate) fn must_connect_to(self) -> bool {
false
}
pub(crate) fn flow(self) -> crate::expr::Flow {
crate::expr::Flow::Source
}
}
impl ValueType for FormalInput {
type Type = CanonicalType;
type ValueCategory = crate::expr::value_category::ValueCategoryExpr;
fn ty(&self) -> Self::Type {
self.0.ty
}
}
impl GetTarget for FormalInput {
fn target(&self) -> Option<Interned<Target>> {
Some(Target::from(*self).intern_sized())
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum FormalKind { pub enum FormalKind {
@ -316,76 +138,110 @@ make_formal!(
hdl_cover hdl_cover
); );
pub trait MakeFormalExpr: Type {}
impl<T: Type> MakeFormalExpr for T {}
#[hdl] #[hdl]
pub fn formal_global_clock() -> Expr<Clock> { pub fn formal_global_clock() -> Expr<Clock> {
static CACHE: OnceLock<Expr<Clock>> = OnceLock::new(); #[hdl_module(extern)]
*CACHE.get_or_init(|| { fn formal_global_clock() {
let kind = FormalInputKind::FormalGlobalClock; #[hdl]
Expr::from_canonical( let clk: Clock = m.output();
FormalInput::new( m.annotate_module(BlackBoxInlineAnnotation {
kind, path: "fayalite_formal_global_clock.v".intern(),
NameId( text: r"module __fayalite_formal_global_clock(output clk);
kind.interned_name(), (* gclk *)
kind.fixed_id().expect("known to have a fixed Id"), reg clk;
), endmodule
Clock.into(), "
kind.fixed_source_location() .intern(),
.expect("known to have a fixed SourceLocation"), });
) m.verilog_name("__fayalite_formal_global_clock");
.to_expr(), }
) #[hdl]
}) let formal_global_clock = instance(formal_global_clock());
formal_global_clock.clk
} }
#[hdl] #[hdl]
pub fn formal_reset() -> Expr<SyncReset> { pub fn formal_reset() -> Expr<SyncReset> {
static CACHE: OnceLock<Expr<SyncReset>> = OnceLock::new(); #[hdl_module(extern)]
*CACHE.get_or_init(|| { fn formal_reset() {
let kind = FormalInputKind::FormalReset; #[hdl]
Expr::from_canonical( let rst: SyncReset = m.output();
FormalInput::new( m.annotate_module(BlackBoxInlineAnnotation {
kind, path: "fayalite_formal_reset.v".intern(),
NameId( text: r"module __fayalite_formal_reset(output rst);
kind.interned_name(), assign rst = $initstate;
kind.fixed_id().expect("known to have a fixed Id"), endmodule
), "
SyncReset.into(), .intern(),
kind.fixed_source_location() });
.expect("known to have a fixed SourceLocation"), m.verilog_name("__fayalite_formal_reset");
) }
.to_expr(), static MOD: OnceLock<Interned<Module<formal_reset>>> = OnceLock::new();
) #[hdl]
}) let formal_reset = instance(*MOD.get_or_init(formal_reset));
formal_reset.rst
} }
macro_rules! make_any_const_fn { macro_rules! make_any_const_fn {
($ident:ident, $ident_with_loc:ident, $verilog_attribute:literal, $kind:ident) => { ($ident:ident, $verilog_attribute:literal) => {
#[track_caller]
#[hdl] #[hdl]
pub fn $ident<T: BoolOrIntType>(ty: T) -> Expr<T> { pub fn $ident<T: BoolOrIntType>(ty: T) -> Expr<T> {
$ident_with_loc(ty, SourceLocation::caller()) #[hdl_module(extern)]
} pub(super) fn $ident<T: BoolOrIntType>(ty: T) {
#[track_caller]
#[hdl] #[hdl]
pub fn $ident_with_loc<T: BoolOrIntType>( let out: T = m.output(ty);
ty: T, let width = ty.width();
source_location: SourceLocation, let verilog_bitslice = if width == 1 {
) -> Expr<T> { String::new()
let kind = FormalInputKind::$kind; } else {
Expr::from_canonical( format!(" [{}:0]", width - 1)
FormalInput::new( };
kind, m.annotate_module(BlackBoxInlineAnnotation {
NameId(kind.interned_name(), crate::module::Id::new()), path: Intern::intern_owned(format!(
ty.canonical(), "fayalite_{}_{width}.v",
source_location, stringify!($ident),
) )),
.to_expr(), text: Intern::intern_owned(format!(
) r"module __fayalite_{}_{width}(output{verilog_bitslice} out);
(* {} *)
reg{verilog_bitslice} out;
endmodule
",
stringify!($ident),
$verilog_attribute,
)),
});
m.verilog_name(format!("__fayalite_{}_{width}", stringify!($ident)));
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct TheMemoize<T>(T);
impl<T: BoolOrIntType> Memoize for TheMemoize<T> {
type Input = ();
type InputOwned = ();
type Output = Option<Interned<Module<$ident<T>>>>;
fn inner(self, _input: &Self::Input) -> Self::Output {
if self.0.width() == 0 {
None
} else {
Some($ident(self.0))
}
}
}
let Some(module) = TheMemoize(ty).get_owned(()) else {
return 0_hdl_u0.cast_bits_to(ty);
};
#[hdl]
let $ident = instance(module);
$ident.out
} }
}; };
} }
make_any_const_fn!(any_const, any_const_with_loc, "anyconst", AnyConst); make_any_const_fn!(any_const, "anyconst");
make_any_const_fn!(any_seq, any_seq_with_loc, "anyseq", AnySeq); make_any_const_fn!(any_seq, "anyseq");
make_any_const_fn!(all_const, all_const_with_loc, "allconst", AllConst); make_any_const_fn!(all_const, "allconst");
make_any_const_fn!(all_seq, all_seq_with_loc, "allseq", AllSeq); make_any_const_fn!(all_seq, "allseq");

File diff suppressed because it is too large Load diff

View file

@ -4,17 +4,17 @@
use crate::{ use crate::{
bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder}, bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder},
expr::{ expr::{
CastBitsTo, CastTo, CastToBits, CastToImpl, Expr, HdlPartialEq, HdlPartialEqImpl, CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd,
HdlPartialOrd, HdlPartialOrdImpl, ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd},
}, },
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType, UIntValue}, int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType},
intern::{Intern, InternSlice, Interned}, intern::{Intern, Interned},
phantom_const::PhantomConst, phantom_const::PhantomConst,
sim::value::{SimValue, ToSimValueWithType}, sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
SimValueDebug, StaticType, Type, TypeProperties, impl_match_variant_as_self, StaticType, Type, TypeProperties, impl_match_variant_as_self,
}, },
}; };
use bitvec::{order::Lsb0, view::BitView}; use bitvec::{order::Lsb0, view::BitView};
@ -22,7 +22,7 @@ use serde::{
Deserialize, Deserializer, Serialize, Serializer, Deserialize, Deserializer, Serialize, Serializer,
de::{Error, Visitor, value::UsizeDeserializer}, de::{Error, Visitor, value::UsizeDeserializer},
}; };
use std::{borrow::Cow, fmt, marker::PhantomData, ops::Index}; use std::{fmt, marker::PhantomData, ops::Index};
const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"]; const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"];
@ -94,17 +94,9 @@ impl Type for UIntInRangeMaskType {
} }
} }
impl SimValueDebug for UIntInRangeMaskType {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl BundleType for UIntInRangeMaskType { impl BundleType for UIntInRangeMaskType {
type Builder = NoBuilder; type Builder = NoBuilder;
type FilledBuilder = Expr<UIntInRangeMaskType>;
fn fields(&self) -> Interned<[BundleField]> { fn fields(&self) -> Interned<[BundleField]> {
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
@ -120,8 +112,8 @@ impl BundleType for UIntInRangeMaskType {
flipped: false, flipped: false,
ty: range.canonical(), ty: range.canonical(),
}, },
] ][..]
.intern_slice() .intern()
} }
} }
@ -144,62 +136,33 @@ impl ToSimValueWithType<UIntInRangeMaskType> for bool {
} }
} }
impl CastToImpl<Bool> for UIntInRangeMaskType { impl ExprCastTo<Bool> for UIntInRangeMaskType {
type ValueOutput = bool; fn cast_to(src: Expr<Self>, to_type: Bool) -> Expr<Bool> {
src.cast_to_bits().cast_to(to_type)
fn cast_value_to(
_this: Self,
value: Cow<'_, Self::SimValue>,
_to_type: Bool,
) -> Self::ValueOutput {
*value
}
fn cast_expr_to(value: Expr<Self>, to_type: Bool) -> Expr<Bool> {
value.cast_to_bits().cast_to(to_type)
} }
} }
impl CastToImpl<UIntInRangeMaskType> for Bool { impl ExprCastTo<UIntInRangeMaskType> for Bool {
type ValueOutput = SimValue<UIntInRangeMaskType>; fn cast_to(src: Expr<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> {
src.cast_to_static::<UInt<1>>().cast_bits_to(to_type)
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<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> {
value.cast_to_static::<UInt<1>>().cast_bits_to(to_type)
} }
} }
impl HdlPartialEqImpl<Self> for UIntInRangeMaskType { impl ExprPartialEq<Self> for UIntInRangeMaskType {
const TRY_STRUCTURAL_EQ: bool = true; fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
#[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<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
} }
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
#[track_caller]
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
} }
} }
impl SimValuePartialEq<Self> for UIntInRangeMaskType {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
}
}
type PhantomConstRangeMaskType = <PhantomConst<SerdeRange<DynSize, DynSize>> as Type>::MaskType; type PhantomConstRangeMaskType = <PhantomConst<SerdeRange<DynSize, DynSize>> as Type>::MaskType;
#[derive(Default, Copy, Clone, Debug)] #[derive(Default, Copy, Clone, Debug)]
@ -337,25 +300,9 @@ macro_rules! define_uint_in_range_type {
} }
} }
pub fn new(start: Start::SizeType, end: End::SizeType) -> Self { pub fn new(start: Start::SizeType, end: End::SizeType) -> Self {
Self::from_phantom_const_range(PhantomConst::new_sized($SerdeRange { start, end })) Self::from_phantom_const_range(PhantomConst::new(
} $SerdeRange { start, end }.intern_sized(),
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
}
}
impl<Start: Size, End: Size> SimValueDebug for $UIntInRangeType<Start, End> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
} }
} }
@ -446,6 +393,7 @@ macro_rules! define_uint_in_range_type {
impl<Start: Size, End: Size> BundleType for $UIntInRangeType<Start, End> { impl<Start: Size, End: Size> BundleType for $UIntInRangeType<Start, End> {
type Builder = NoBuilder; type Builder = NoBuilder;
type FilledBuilder = Expr<Self>;
fn fields(&self) -> Interned<[BundleField]> { fn fields(&self) -> Interned<[BundleField]> {
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
@ -461,8 +409,8 @@ macro_rules! define_uint_in_range_type {
flipped: false, flipped: false,
ty: range.canonical(), ty: range.canonical(),
}, },
] ][..]
.intern_slice() .intern()
} }
} }
@ -529,66 +477,32 @@ macro_rules! define_uint_in_range_type {
} }
} }
impl<Start: Size, End: Size, Width: Size> CastToImpl<UIntType<Width>> impl<Start: Size, End: Size> ExprCastTo<UInt> for $UIntInRangeType<Start, End> {
for $UIntInRangeType<Start, End> fn cast_to(src: Expr<Self>, to_type: UInt) -> Expr<UInt> {
{ src.cast_to_bits().cast_to(to_type)
type ValueOutput = UIntValue<Width>;
fn cast_value_to(
_this: Self,
value: Cow<'_, Self::SimValue>,
to_type: UIntType<Width>,
) -> Self::ValueOutput {
value.cast_to(to_type)
}
fn cast_expr_to(value: Expr<Self>, to_type: UIntType<Width>) -> Expr<UIntType<Width>> {
value.cast_to_bits().cast_to(to_type)
} }
} }
impl<Start: Size, End: Size, Width: Size> CastToImpl<$UIntInRangeType<Start, End>> impl<Start: Size, End: Size> ExprCastTo<$UIntInRangeType<Start, End>> for UInt {
for UIntType<Width> fn cast_to(
{ src: Expr<Self>,
type ValueOutput = SimValue<$UIntInRangeType<Start, End>>;
fn cast_value_to(
_this: Self,
value: Cow<'_, Self::SimValue>,
to_type: $UIntInRangeType<Start, End>,
) -> Self::ValueOutput {
value.cast_to(to_type.value).cast_bits_to(to_type)
}
fn cast_expr_to(
value: Expr<Self>,
to_type: $UIntInRangeType<Start, End>, to_type: $UIntInRangeType<Start, End>,
) -> Expr<$UIntInRangeType<Start, End>> { ) -> Expr<$UIntInRangeType<Start, End>> {
value.cast_to(to_type.value).cast_bits_to(to_type) src.cast_bits_to(to_type)
} }
} }
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size> impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
HdlPartialEqImpl<$UIntInRangeType<RhsStart, RhsEnd>> ExprPartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd> for $UIntInRangeType<LhsStart, LhsEnd>
{ {
const TRY_STRUCTURAL_EQ: bool = true; fn cmp_eq(
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
*lhs_value == *rhs_value
}
fn cmp_expr_eq(
lhs: Expr<Self>, lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> { ) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
} }
fn cmp_expr_ne( fn cmp_ne(
lhs: Expr<Self>, lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> { ) -> Expr<Bool> {
@ -597,60 +511,28 @@ macro_rules! define_uint_in_range_type {
} }
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size> impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
HdlPartialOrdImpl<$UIntInRangeType<RhsStart, RhsEnd>> ExprPartialOrd<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd> for $UIntInRangeType<LhsStart, LhsEnd>
{ {
fn cmp_value_lt( fn cmp_lt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
PartialOrd::lt(&*lhs_value, &*rhs_value)
}
fn cmp_value_le(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
PartialOrd::le(&*lhs_value, &*rhs_value)
}
fn cmp_value_gt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
PartialOrd::gt(&*lhs_value, &*rhs_value)
}
fn cmp_value_ge(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
PartialOrd::ge(&*lhs_value, &*rhs_value)
}
fn cmp_expr_lt(
lhs: Expr<Self>, lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> { ) -> Expr<Bool> {
lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits()) lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits())
} }
fn cmp_expr_le( fn cmp_le(
lhs: Expr<Self>, lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> { ) -> Expr<Bool> {
lhs.cast_to_bits().cmp_le(rhs.cast_to_bits()) lhs.cast_to_bits().cmp_le(rhs.cast_to_bits())
} }
fn cmp_expr_gt( fn cmp_gt(
lhs: Expr<Self>, lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> { ) -> Expr<Bool> {
lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits()) lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits())
} }
fn cmp_expr_ge( fn cmp_ge(
lhs: Expr<Self>, lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> { ) -> Expr<Bool> {
@ -658,142 +540,70 @@ macro_rules! define_uint_in_range_type {
} }
} }
impl<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<UIntType<Width>> impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
SimValuePartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd>
{
fn sim_value_eq(
this: &SimValue<Self>,
other: &SimValue<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> bool {
**this == **other
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialEq<UIntType<Width>>
for $UIntInRangeType<Start, End> for $UIntInRangeType<Start, End>
{ {
const TRY_STRUCTURAL_EQ: bool = false; fn cmp_eq(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialEq::cmp_eq(&*lhs_value, &*rhs_value)
}
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs) lhs.cast_to_bits().cmp_eq(rhs)
} }
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { fn cmp_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs) lhs.cast_to_bits().cmp_ne(rhs)
} }
} }
impl<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<$UIntInRangeType<Start, End>> impl<Start: Size, End: Size, Width: Size> ExprPartialEq<$UIntInRangeType<Start, End>>
for UIntType<Width> for UIntType<Width>
{ {
const TRY_STRUCTURAL_EQ: bool = false; fn cmp_eq(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialEq::cmp_eq(&*lhs_value, *rhs_value)
}
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_eq(rhs.cast_to_bits()) lhs.cmp_eq(rhs.cast_to_bits())
} }
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { fn cmp_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_ne(rhs.cast_to_bits()) lhs.cmp_ne(rhs.cast_to_bits())
} }
} }
impl<Start: Size, End: Size, Width: Size> HdlPartialOrdImpl<UIntType<Width>> impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<UIntType<Width>>
for $UIntInRangeType<Start, End> for $UIntInRangeType<Start, End>
{ {
fn cmp_value_lt( fn cmp_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialOrd::cmp_lt(&*lhs_value, &*rhs_value)
}
fn cmp_value_le(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialOrd::cmp_le(&*lhs_value, &*rhs_value)
}
fn cmp_value_gt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialOrd::cmp_gt(&*lhs_value, &*rhs_value)
}
fn cmp_value_ge(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialOrd::cmp_ge(&*lhs_value, &*rhs_value)
}
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_lt(rhs) lhs.cast_to_bits().cmp_lt(rhs)
} }
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { fn cmp_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_le(rhs) lhs.cast_to_bits().cmp_le(rhs)
} }
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { fn cmp_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_gt(rhs) lhs.cast_to_bits().cmp_gt(rhs)
} }
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { fn cmp_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ge(rhs) lhs.cast_to_bits().cmp_ge(rhs)
} }
} }
impl<Start: Size, End: Size, Width: Size> HdlPartialOrdImpl<$UIntInRangeType<Start, End>> impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<$UIntInRangeType<Start, End>>
for UIntType<Width> for UIntType<Width>
{ {
fn cmp_value_lt( fn cmp_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialOrd::cmp_lt(&*lhs_value, *rhs_value)
}
fn cmp_value_le(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialOrd::cmp_le(&*lhs_value, *rhs_value)
}
fn cmp_value_gt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialOrd::cmp_gt(&*lhs_value, *rhs_value)
}
fn cmp_value_ge(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialOrd::cmp_ge(&*lhs_value, *rhs_value)
}
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_lt(rhs.cast_to_bits()) lhs.cmp_lt(rhs.cast_to_bits())
} }
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { fn cmp_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_le(rhs.cast_to_bits()) lhs.cmp_le(rhs.cast_to_bits())
} }
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { fn cmp_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_gt(rhs.cast_to_bits()) lhs.cmp_gt(rhs.cast_to_bits())
} }
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { fn cmp_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_ge(rhs.cast_to_bits()) lhs.cmp_ge(rhs.cast_to_bits())
} }
} }

View file

@ -4,191 +4,66 @@
use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher}; use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher};
use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec}; use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec};
use hashbrown::HashTable; use hashbrown::HashTable;
use once_cell::race::OnceRef;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
borrow::{Borrow, Cow}, borrow::{Borrow, Cow},
cell::RefCell,
cmp::Ordering, cmp::Ordering,
ffi::{OsStr, OsString},
fmt, fmt,
hash::{BuildHasher, Hash, Hasher}, hash::{BuildHasher, Hash, Hasher},
iter::FusedIterator, iter::FusedIterator,
marker::PhantomData,
ops::Deref, ops::Deref,
path::{Path, PathBuf}, sync::{Mutex, RwLock},
sync::RwLock,
}; };
mod interner;
mod type_map; mod type_map;
/// invariant: T must be zero-sized, `type_id` is unique for every possible T value. pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any {
struct LazyInternedLazyInner<T: ?Sized + 'static> { fn get(&self) -> Interned<T>;
type_id: TypeId,
value: T,
} }
impl<T: ?Sized + 'static> Hash for LazyInternedLazyInner<T> { impl<T: ?Sized + Send + Sync + 'static, F: ?Sized + Fn() -> Interned<T> + Send + Sync + Any>
fn hash<H: Hasher>(&self, state: &mut H) { LazyInternedTrait<T> for F
let Self { type_id, value: _ } = self; {
type_id.hash(state); fn get(&self) -> Interned<T> {
self()
} }
} }
impl<T: ?Sized + 'static> PartialEq for LazyInternedLazyInner<T> { #[repr(transparent)]
fn eq(&self, other: &Self) -> bool { pub struct LazyInternedFn<T: ?Sized + Send + Sync + 'static>(pub &'static dyn LazyInternedTrait<T>);
let Self { type_id, value: _ } = self;
*type_id == other.type_id
}
}
impl<T: ?Sized + 'static> Eq for LazyInternedLazyInner<T> {} impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedFn<T> {}
impl<T: ?Sized + 'static> fmt::Debug for LazyInternedLazyInner<T> { impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedFn<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LazyInternedLazyInner")
.finish_non_exhaustive()
}
}
impl<T: ?Sized + 'static> LazyInternedLazyInner<T> {
const fn new(value: T) -> Self
where
T: Sized,
{
const { assert!(size_of::<T>() == 0) };
Self {
type_id: TypeId::of::<T>(),
value,
}
}
}
pub struct LazyInternedLazy<T: ?Sized + Send + Sync + 'static>(
&'static LazyInternedLazyInner<dyn Fn() -> Interned<T> + Send + Sync>,
);
impl<T: ?Sized + Send + Sync + 'static> LazyInternedLazy<T> {
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self(&const { LazyInternedLazyInner::new(|| V::default().into()) })
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn interned(self) -> Interned<T> {
struct Map(hashbrown::HashTable<(TypeId, &'static (dyn Any + Send + Sync))>);
impl Map {
const EMPTY: Self = Self(hashbrown::HashTable::new());
fn get<T: ?Sized + Send + Sync + 'static>(
&self,
lazy_interned_lazy: LazyInternedLazy<T>,
hash: u64,
) -> Option<&'static Interned<T>> {
let &(_, v) = self.0.find(hash, |v| v.0 == lazy_interned_lazy.0.type_id)?;
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
unreachable!();
};
Some(retval)
}
fn get_or_insert<T: ?Sized + Send + Sync + 'static>(
&mut self,
lazy_interned_lazy: LazyInternedLazy<T>,
hash: u64,
v: &'static Interned<T>,
) -> &'static Interned<T> {
let entry = self
.0
.entry(
hash,
|&(k, _)| k == lazy_interned_lazy.0.type_id,
|&(k, _)| type_map::TypeIdBuildHasher.hash_one(k),
)
.or_insert_with(|| (lazy_interned_lazy.0.type_id, v));
let &(_, v) = entry.get();
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
unreachable!();
};
retval
}
}
static GLOBAL_CACHE: RwLock<Map> = RwLock::new(Map::EMPTY);
#[cold]
fn insert_in_thread_local_cache<T: ?Sized + Send + Sync + 'static>(
cache: &RefCell<Map>,
this: LazyInternedLazy<T>,
hash: u64,
) -> Interned<T> {
let read_lock = GLOBAL_CACHE.read().unwrap();
let v = read_lock.get(this, hash);
drop(read_lock);
let v = v.unwrap_or_else(|| {
let v = Box::leak(Box::new((this.0.value)()));
GLOBAL_CACHE.write().unwrap().get_or_insert(this, hash, v)
});
*cache.borrow_mut().get_or_insert(this, hash, v)
}
thread_local! {
static THREAD_LOCAL_CACHE: RefCell<Map> = const { RefCell::new(Map::EMPTY) };
}
let hash = type_map::TypeIdBuildHasher.hash_one(self.0.type_id);
THREAD_LOCAL_CACHE.with(|cache| {
let borrow = cache.borrow();
if let Some(v) = borrow.get(self, hash) {
*v
} else {
drop(borrow);
insert_in_thread_local_cache(cache, self, hash)
}
})
}
}
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedLazy<T> {}
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedLazy<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
*self *self
} }
} }
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedLazy<T> { impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedFn<T> {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state); self.0.get_ptr_eq_with_type_id().hash(state);
} }
} }
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedLazy<T> {} impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedFn<T> {}
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedLazy<T> { impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedFn<T> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.0 == other.0 self.0.get_ptr_eq_with_type_id() == other.0.get_ptr_eq_with_type_id()
} }
} }
pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> { pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> {
Interned(Interned<T>), Interned(Interned<T>),
Lazy(LazyInternedLazy<T>), Lazy(LazyInternedFn<T>),
} }
impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> { impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> {
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self { pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
Self::Lazy(LazyInternedLazy::new_const::<V>()) Self::Lazy(LazyInternedFn(v))
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn interned(self) -> Interned<T> {
match self {
Self::Interned(retval) => retval,
Self::Lazy(retval) => retval.interned(),
}
} }
} }
@ -200,7 +75,7 @@ impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> {
impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {} impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {}
impl<T: ?Sized + Sync + Send + 'static> Deref for LazyInterned<T> { impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -208,9 +83,9 @@ impl<T: ?Sized + Sync + Send + 'static> Deref for LazyInterned<T> {
} }
} }
impl<T: ?Sized + Sync + Send + 'static> Eq for LazyInterned<T> where Interned<T>: Eq {} impl<T: ?Sized + Sync + Send + 'static + Intern> Eq for LazyInterned<T> where Interned<T>: Eq {}
impl<T: ?Sized + Sync + Send + 'static> PartialEq for LazyInterned<T> impl<T: ?Sized + Sync + Send + 'static + Intern> PartialEq for LazyInterned<T>
where where
Interned<T>: PartialEq, Interned<T>: PartialEq,
{ {
@ -219,7 +94,7 @@ where
} }
} }
impl<T: ?Sized + Sync + Send + 'static> Ord for LazyInterned<T> impl<T: ?Sized + Sync + Send + 'static + Intern> Ord for LazyInterned<T>
where where
Interned<T>: Ord, Interned<T>: Ord,
{ {
@ -228,7 +103,7 @@ where
} }
} }
impl<T: ?Sized + Sync + Send + 'static> PartialOrd for LazyInterned<T> impl<T: ?Sized + Sync + Send + 'static + Intern> PartialOrd for LazyInterned<T>
where where
Interned<T>: PartialOrd, Interned<T>: PartialOrd,
{ {
@ -237,7 +112,7 @@ where
} }
} }
impl<T: ?Sized + Sync + Send + 'static> Hash for LazyInterned<T> impl<T: ?Sized + Sync + Send + 'static + Intern> Hash for LazyInterned<T>
where where
Interned<T>: Hash, Interned<T>: Hash,
{ {
@ -246,6 +121,77 @@ where
} }
} }
impl<T: ?Sized + Sync + Send + 'static> LazyInterned<T> {
pub fn interned(self) -> Interned<T>
where
T: Intern,
{
struct MemoizeInterned<T: ?Sized + Intern>(PhantomData<T>);
impl<T: ?Sized + Intern> Hash for MemoizeInterned<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<T: ?Sized + Intern> PartialEq for MemoizeInterned<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T: ?Sized + Intern> Eq for MemoizeInterned<T> {}
impl<T: ?Sized + Intern> Clone for MemoizeInterned<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Intern> Copy for MemoizeInterned<T> {}
impl<T: ?Sized + Intern> MemoizeGeneric for MemoizeInterned<T> {
type InputRef<'a> = LazyInternedFn<T>;
type InputOwned = LazyInternedFn<T>;
type InputCow<'a> = LazyInternedFn<T>;
type Output = Interned<T>;
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
a == b
}
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
*input
}
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
input
}
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
*input
}
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
input
}
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
input
}
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
input.0.get()
}
}
match self {
Self::Interned(retval) => retval,
Self::Lazy(retval) => MemoizeInterned(PhantomData).get(retval),
}
}
}
pub trait InternedCompare { pub trait InternedCompare {
type InternedCompareKey: Ord + Hash; type InternedCompareKey: Ord + Hash;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey; fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey;
@ -341,266 +287,15 @@ impl InternedCompare for BitSlice {
} }
} }
/// Safety: `as_bytes` and `from_bytes_unchecked` must return the same pointer as the input. impl InternedCompare for str {
/// all values returned by `as_bytes` must be valid to pass to `from_bytes_unchecked`. type InternedCompareKey = PtrEqWithMetadata<Self>;
/// `into_bytes` must return the exact same thing as `as_bytes`.
/// `Interned<Self>` 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<u8>;
/// 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 { fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
PtrEqWithMetadata(InternStrLike::as_bytes(this)) PtrEqWithMetadata(this)
}
}
impl Intern for $ty {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self> {
Interned::cast_unchecked(
<[u8]>::intern_cow(match this {
Cow::Borrowed(v) => Cow::Borrowed(<Self as InternStrLike>::as_bytes(v)),
Cow::Owned(v) => {
// verify $Owned is correct
let v: $Owned = v;
Cow::Owned(<Self as InternStrLike>::into_bytes(v))
}
}),
// Safety: guaranteed safe because we got the bytes from `as_bytes`/`into_bytes`
|v| unsafe { <Self as InternStrLike>::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<D>(deserializer: D) -> Result<Self, D::Error>
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<Interned<$ty>> for $Owned {
fn from(v: Interned<$ty>) -> Self {
Interned::into_inner(v).into()
}
}
impl From<Interned<$ty>> 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<u8> {
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<u8> {
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<u8> {
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<str> {
pub fn from_utf8(v: Interned<[u8]>) -> Result<Self, std::str::Utf8Error> {
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<OsStr> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
pub fn as_interned_path(self) -> Interned<Path> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
}
impl From<Interned<str>> for Interned<OsStr> {
fn from(value: Interned<str>) -> Self {
value.as_interned_os_str()
}
}
impl From<Interned<str>> for Interned<Path> {
fn from(value: Interned<str>) -> Self {
value.as_interned_path()
}
}
impl Interned<OsStr> {
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<str>> {
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<Path> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
}
impl From<Interned<OsStr>> for Interned<Path> {
fn from(value: Interned<OsStr>) -> Self {
value.as_interned_path()
}
}
impl Interned<Path> {
pub fn as_interned_os_str(self) -> Interned<OsStr> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
pub fn to_interned_str(self) -> Option<Interned<str>> {
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<Interned<OsStr>> {
Some(self.file_name()?.intern())
}
}
impl From<Interned<Path>> for Interned<OsStr> {
fn from(value: Interned<Path>) -> 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<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for Box<[T]> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.into_vec().intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for Vec<T> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern_deref()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for &'_ [T] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for &'_ mut [T] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for [T; N] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
(&self).intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for Box<[T; N]> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
let this: Box<[T]> = self;
this.intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for &'_ [T; N] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
let this: &[T] = self;
this.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> 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 { pub trait Intern: Any + Send + Sync {
fn intern(&self) -> Interned<Self>; fn intern(&self) -> Interned<Self>;
fn intern_deref(self) -> Interned<Self::Target>
where
Self: Sized + Deref<Target: Intern + ToOwned<Owned = Self>>,
{
Self::Target::intern_owned(self)
}
fn intern_sized(self) -> Interned<Self> fn intern_sized(self) -> Interned<Self>
where where
Self: Clone, Self: Clone,
@ -621,27 +316,74 @@ pub trait Intern: Any + Send + Sync {
} }
} }
impl<T: ?Sized + Intern + ToOwned> From<Cow<'_, T>> for Interned<T> { struct InternerState<T: ?Sized + 'static + Send + Sync> {
fn from(value: Cow<'_, T>) -> Self { table: HashTable<&'static T>,
Intern::intern_cow(value) hasher: DefaultBuildHasher,
}
pub struct Interner<T: ?Sized + 'static + Send + Sync> {
state: Mutex<InternerState<T>>,
}
impl<T: ?Sized + 'static + Send + Sync> Interner<T> {
fn get() -> &'static Interner<T> {
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
TYPE_ID_MAP.get_or_insert_default()
} }
} }
impl<T: ?Sized + Intern> From<&'_ T> for Interned<T> { impl<T: ?Sized + 'static + Send + Sync> Default for Interner<T> {
fn from(value: &'_ T) -> Self { fn default() -> Self {
Intern::intern(value) Self {
state: Mutex::new(InternerState {
table: HashTable::new(),
hasher: Default::default(),
}),
}
} }
} }
impl<T: Intern + Clone> From<T> for Interned<T> { impl<T: ?Sized + 'static + Send + Sync + Hash + Eq + ToOwned> Interner<T> {
fn from(value: T) -> Self { fn intern<F: FnOnce(Cow<'_, T>) -> &'static T>(
Intern::intern_sized(value) &self,
alloc: F,
value: Cow<'_, T>,
) -> Interned<T> {
let mut state = self.state.lock().unwrap();
let InternerState { table, hasher } = &mut *state;
let inner = *table
.entry(
hasher.hash_one(&*value),
|k| **k == *value,
|k| hasher.hash_one(&**k),
)
.or_insert_with(|| alloc(value))
.get();
Interned { inner }
} }
} }
impl<T: ?Sized + 'static + Send + Sync + ToOwned> From<Interned<T>> for Cow<'_, T> { impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<T> {
fn from(value: Interned<T>) -> Self { fn intern_sized(&self, value: Cow<'_, T>) -> Interned<T> {
Cow::Borrowed(Interned::into_inner(value)) self.intern(|value| Box::leak(Box::new(value.into_owned())), value)
}
}
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<[T]> {
fn intern_slice(&self, value: Cow<'_, [T]>) -> Interned<[T]> {
self.intern(|value| value.into_owned().leak(), value)
}
}
impl Interner<BitSlice> {
fn intern_bit_slice(&self, value: Cow<'_, BitSlice>) -> Interned<BitSlice> {
self.intern(|value| value.into_owned().leak(), value)
}
}
impl Interner<str> {
fn intern_str(&self, value: Cow<'_, str>) -> Interned<str> {
self.intern(|value| value.into_owned().leak(), value)
} }
} }
@ -674,70 +416,29 @@ forward_fmt_trait!(Pointer);
forward_fmt_trait!(UpperExp); forward_fmt_trait!(UpperExp);
forward_fmt_trait!(UpperHex); forward_fmt_trait!(UpperHex);
impl<T: ?Sized + 'static + Send + Sync + AsRef<U>, U: ?Sized> AsRef<U> for Interned<T> {
fn as_ref(&self) -> &U {
T::as_ref(self)
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct InternedSliceIter<T: Clone + 'static + Send + Sync> { pub struct InternedSliceIter<T: Clone + 'static + Send + Sync> {
iter: std::iter::Cloned<std::slice::Iter<'static, T>>, slice: Interned<[T]>,
} index: std::ops::Range<usize>,
impl<T: Clone + 'static + Send + Sync> Default for InternedSliceIter<T> {
fn default() -> Self {
Self {
iter: [].iter().cloned(),
}
}
} }
impl<T: Clone + 'static + Send + Sync> Iterator for InternedSliceIter<T> { impl<T: Clone + 'static + Send + Sync> Iterator for InternedSliceIter<T> {
type Item = T; type Item = T;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.iter.next() self.index.next().map(|index| self.slice[index].clone())
} }
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint() self.index.size_hint()
}
fn count(self) -> usize {
self.iter.count()
}
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.iter.nth(n)
}
fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.iter.fold(init, f)
} }
} }
impl<T: Clone + 'static + Send + Sync> DoubleEndedIterator for InternedSliceIter<T> { impl<T: Clone + 'static + Send + Sync> DoubleEndedIterator for InternedSliceIter<T> {
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back() self.index
} .next_back()
.map(|index| self.slice[index].clone())
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.iter.nth_back(n)
}
fn rfold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.iter.rfold(init, f)
} }
} }
@ -751,7 +452,8 @@ impl<T: Clone + 'static + Send + Sync> IntoIterator for Interned<[T]> {
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
InternedSliceIter { InternedSliceIter {
iter: Interned::into_inner(self).iter().cloned(), index: 0..self.len(),
slice: self,
} }
} }
} }
@ -783,57 +485,6 @@ where
} }
} }
impl<I> FromIterator<I> for Interned<str>
where
String: FromIterator<I>,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
String::from_iter(iter).intern_deref()
}
}
impl<I> FromIterator<I> for Interned<Path>
where
PathBuf: FromIterator<I>,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
PathBuf::from_iter(iter).intern_deref()
}
}
impl<I> FromIterator<I> for Interned<OsStr>
where
OsString: FromIterator<I>,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
OsString::from_iter(iter).intern_deref()
}
}
impl From<Interned<str>> for clap::builder::Str {
fn from(value: Interned<str>) -> Self {
Interned::into_inner(value).into()
}
}
impl From<Interned<str>> for clap::builder::OsStr {
fn from(value: Interned<str>) -> Self {
Interned::into_inner(value).into()
}
}
impl From<Interned<str>> for clap::builder::StyledStr {
fn from(value: Interned<str>) -> Self {
Interned::into_inner(value).into()
}
}
impl From<Interned<str>> for clap::Id {
fn from(value: Interned<str>) -> Self {
Interned::into_inner(value).into()
}
}
impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Vec<T> { impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Vec<T> {
fn from(value: Interned<[T]>) -> Self { fn from(value: Interned<[T]>) -> Self {
Vec::from(&*value) Vec::from(&*value)
@ -846,12 +497,24 @@ impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Box<[T]> {
} }
} }
impl From<Interned<str>> for String {
fn from(value: Interned<str>) -> Self {
String::from(&*value)
}
}
impl<I> Default for Interned<[I]> impl<I> Default for Interned<[I]>
where where
[I]: Intern, [I]: Intern,
{ {
fn default() -> Self { fn default() -> Self {
Intern::intern(&[]) [][..].intern()
}
}
impl Default for Interned<str> {
fn default() -> Self {
"".intern()
} }
} }
@ -982,6 +645,15 @@ impl<'de> Deserialize<'de> for Interned<BitSlice> {
} }
} }
impl<'de> Deserialize<'de> for Interned<str> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
String::deserialize(deserializer).map(Intern::intern_owned)
}
}
impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T { impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T {
fn intern(&self) -> Interned<Self> { fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self)) Self::intern_cow(Cow::Borrowed(self))
@ -998,7 +670,7 @@ impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T {
where where
Self: ToOwned, Self: ToOwned,
{ {
interner::Interner::get().intern_sized(this) Interner::get().intern_sized(this)
} }
} }
@ -1018,7 +690,7 @@ impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for [T] {
where where
Self: ToOwned, Self: ToOwned,
{ {
interner::Interner::get().intern_slice(this) Interner::get().intern_slice(this)
} }
} }
@ -1038,7 +710,27 @@ impl Intern for BitSlice {
where where
Self: ToOwned, Self: ToOwned,
{ {
interner::Interner::get().intern_bit_slice(this) Interner::get().intern_bit_slice(this)
}
}
impl Intern for str {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_owned(this: <Self as ToOwned>::Owned) -> Interned<Self>
where
Self: ToOwned,
{
Self::intern_cow(Cow::Owned(this))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self>
where
Self: ToOwned,
{
Interner::get().intern_str(this)
} }
} }
@ -1056,17 +748,10 @@ pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy {
fn inner(self, input: Self::InputRef<'_>) -> Self::Output; fn inner(self, input: Self::InputRef<'_>) -> Self::Output;
fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output { fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output {
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new(); static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
thread_local! {
static TYPE_ID_MAP_CACHE: TypeIdMap = const { TypeIdMap::new() };
}
let map: &RwLock<( let map: &RwLock<(
DefaultBuildHasher, DefaultBuildHasher,
HashTable<(Self, Self::InputOwned, Self::Output)>, HashTable<(Self, Self::InputOwned, Self::Output)>,
)> = TYPE_ID_MAP_CACHE.with(|cache| { )> = TYPE_ID_MAP.get_or_insert_default();
cache.get_or_insert_with(|| {
TYPE_ID_MAP.get_or_insert_with(|| Box::leak(Default::default()))
})
});
fn hash_eq_key<'a, 'b, T: MemoizeGeneric>( fn hash_eq_key<'a, 'b, T: MemoizeGeneric>(
this: &'a T, this: &'a T,
input: T::InputRef<'b>, input: T::InputRef<'b>,
@ -1168,35 +853,3 @@ pub trait Memoize: 'static + Send + Sync + Hash + Eq + Copy {
self.get_cow(Cow::Borrowed(input)) self.get_cow(Cow::Borrowed(input))
} }
} }
/// like `once_cell::race::OnceBox` but for `Interned<T>` instead of `Box<T>`
pub struct OnceInterned<T: 'static + Send + Sync>(OnceRef<'static, T>);
impl<T: 'static + Send + Sync + fmt::Debug> fmt::Debug for OnceInterned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OnceInterned").field(&self.get()).finish()
}
}
impl<T: 'static + Send + Sync> Default for OnceInterned<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: 'static + Send + Sync> OnceInterned<T> {
pub const fn new() -> Self {
Self(OnceRef::new())
}
pub fn set(&self, v: Interned<T>) -> Result<(), ()> {
self.0.set(v.inner)
}
pub fn get(&self) -> Option<Interned<T>> {
self.0.get().map(|inner| Interned { inner })
}
pub fn get_or_init<F: FnOnce() -> Interned<T>>(&self, f: F) -> Interned<T> {
Interned {
inner: self.0.get_or_init(|| f().inner),
}
}
}

View file

@ -1,117 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
intern::{Interned, type_map::TypeIdMap},
util::DefaultBuildHasher,
};
use bitvec::slice::BitSlice;
use hashbrown::HashTable;
use std::{
borrow::Cow,
hash::{BuildHasher, Hash},
sync::RwLock,
};
struct InternerShard<T: ?Sized + 'static + Send + Sync> {
table: HashTable<&'static T>,
}
const LOG2_SHARD_COUNT: u32 = 6;
fn shard_index_from_hash(hash: u64) -> usize {
// number of bits used for hashbrown's Tag
const HASH_BROWN_TAG_BITS: u32 = 7;
// try to extract bits of the hash that hashbrown isn't using,
// while accounting for some hash functions only returning `usize` bits.
const SHARD_INDEX_START: u32 = usize::BITS
.saturating_sub(HASH_BROWN_TAG_BITS)
.saturating_sub(LOG2_SHARD_COUNT);
let mut shard_index = hash >> SHARD_INDEX_START;
shard_index %= 1 << LOG2_SHARD_COUNT;
shard_index as usize
}
pub(crate) struct Interner<T: ?Sized + 'static + Send + Sync> {
shards: [RwLock<InternerShard<T>>; 1 << LOG2_SHARD_COUNT],
hasher: DefaultBuildHasher,
}
impl<T: ?Sized + 'static + Send + Sync> Interner<T> {
pub(crate) fn get() -> &'static Interner<T> {
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
thread_local! {
static TYPE_ID_MAP_CACHE: TypeIdMap = const { TypeIdMap::new() };
}
TYPE_ID_MAP_CACHE.with(|cache| {
cache.get_or_insert_with(|| {
TYPE_ID_MAP.get_or_insert_with(|| Box::leak(Default::default()))
})
})
}
}
impl<T: ?Sized + 'static + Send + Sync> Default for Interner<T> {
fn default() -> Self {
Self {
shards: [const {
RwLock::new(InternerShard {
table: HashTable::new(),
})
}; _],
hasher: Default::default(),
}
}
}
impl<T: ?Sized + 'static + Send + Sync + Hash + Eq + ToOwned> Interner<T> {
fn intern<F: FnOnce(Cow<'_, T>) -> &'static T>(
&self,
alloc: F,
value: Cow<'_, T>,
) -> Interned<T> {
let hash = self.hasher.hash_one(&*value);
let shard_index = shard_index_from_hash(hash);
let shard = &self.shards[shard_index];
let shard_read = shard.read().unwrap();
let Some(&inner) = shard_read.table.find(hash, |k| **k == *value) else {
drop(shard_read);
return self.intern_cold(alloc, value, hash, shard);
};
Interned { inner }
}
#[cold]
fn intern_cold<F: FnOnce(Cow<'_, T>) -> &'static T>(
&self,
alloc: F,
value: Cow<'_, T>,
hash: u64,
shard: &RwLock<InternerShard<T>>,
) -> Interned<T> {
let mut shard = shard.write().unwrap();
let inner = *shard
.table
.entry(hash, |k| **k == *value, |k| self.hasher.hash_one(&**k))
.or_insert_with(|| alloc(value))
.get();
Interned { inner }
}
}
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<T> {
pub(crate) fn intern_sized(&self, value: Cow<'_, T>) -> Interned<T> {
self.intern(|value| Box::leak(Box::new(value.into_owned())), value)
}
}
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<[T]> {
pub(crate) fn intern_slice(&self, value: Cow<'_, [T]>) -> Interned<[T]> {
self.intern(|value| value.into_owned().leak(), value)
}
}
impl Interner<BitSlice> {
pub(crate) fn intern_bit_slice(&self, value: Cow<'_, BitSlice>) -> Interned<BitSlice> {
self.intern(|value| value.into_owned().leak(), value)
}
}

View file

@ -6,7 +6,7 @@ use std::{
sync::RwLock, sync::RwLock,
}; };
pub(crate) struct TypeIdHasher(u64); struct TypeIdHasher(u64);
// assumes TypeId has at least 64 bits that is a good hash // assumes TypeId has at least 64 bits that is a good hash
impl Hasher for TypeIdHasher { impl Hasher for TypeIdHasher {
@ -63,7 +63,7 @@ impl Hasher for TypeIdHasher {
} }
} }
pub(crate) struct TypeIdBuildHasher; struct TypeIdBuildHasher;
impl BuildHasher for TypeIdBuildHasher { impl BuildHasher for TypeIdBuildHasher {
type Hasher = TypeIdHasher; type Hasher = TypeIdHasher;
@ -87,23 +87,20 @@ impl TypeIdMap {
fn insert_slow( fn insert_slow(
&self, &self,
type_id: TypeId, type_id: TypeId,
make: impl FnOnce() -> &'static (dyn Any + Sync + Send), make: fn() -> Box<dyn Any + Sync + Send>,
) -> &'static (dyn Any + Sync + Send) { ) -> &'static (dyn Any + Sync + Send) {
let value = make(); let value = Box::leak(make());
let mut write_guard = self.0.write().unwrap(); let mut write_guard = self.0.write().unwrap();
*write_guard.entry(type_id).or_insert(value) *write_guard.entry(type_id).or_insert(value)
} }
pub(crate) fn get_or_insert_with<T: Sized + Any + Send + Sync>( pub(crate) fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T {
&self,
make: impl FnOnce() -> &'static T,
) -> &'static T {
let type_id = TypeId::of::<T>(); let type_id = TypeId::of::<T>();
let read_guard = self.0.read().unwrap(); let read_guard = self.0.read().unwrap();
let retval = read_guard.get(&type_id).map(|v| *v); let retval = read_guard.get(&type_id).map(|v| *v);
drop(read_guard); drop(read_guard);
let retval = match retval { let retval = match retval {
Some(retval) => retval, Some(retval) => retval,
None => self.insert_slow(type_id, move || make()), None => self.insert_slow(type_id, move || Box::new(T::default())),
}; };
retval.downcast_ref().expect("known to have correct TypeId") retval.downcast_ref().expect("known to have correct TypeId")
} }

View file

@ -4,18 +4,6 @@
// TODO: enable: // TODO: enable:
// #![warn(missing_docs)] // #![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] //! [Main Documentation][_docs]
extern crate self as fayalite; extern crate self as fayalite;
@ -86,135 +74,6 @@ macro_rules! __cfg_expansion_helper {
pub use fayalite_proc_macros::hdl_module; pub use fayalite_proc_macros::hdl_module;
#[doc(inline)] #[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<T: Type> {
/// # v: T,
/// # }
/// #[hdl]
/// pub type MyType<T: Type> = MyStruct<T>;
///
/// // 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<Config>` and returns `Array<Bundle>`
/// #[hdl(get(|config| Array[config.bar][config.foo]))]
/// pub type GetMyArray<P: PhantomConstGet<Config>> = Array<Bundle>;
///
/// // you can then use it in other types:
///
/// #[hdl(no_static)]
/// pub struct WrapMyArray<P: PhantomConstGet<Config>> {
/// pub my_array: GetMyArray<P>,
/// }
///
/// // 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<ConfigItem>,
/// }
///
/// // the expression inside `get` is called with `Interned<Config>` and returns `usize` (not DynSize)
/// #[hdl(get(|config| config.items.len()))]
/// pub type GetItemsLen<P: PhantomConstGet<Config>> = DynSize; // must be DynSize
///
/// // you can then use it in other types:
///
/// #[hdl(no_static)]
/// pub struct FlagPerItem<P: PhantomConstGet<Config>> {
/// pub flags: ArrayType<Bool, GetItemsLen<P>>,
/// }
///
/// // 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 fayalite_proc_macros::hdl;
pub use bitvec; pub use bitvec;
@ -228,8 +87,8 @@ pub mod _docs;
pub mod annotations; pub mod annotations;
pub mod array; pub mod array;
pub mod build;
pub mod bundle; pub mod bundle;
pub mod cli;
pub mod clock; pub mod clock;
pub mod enum_; pub mod enum_;
pub mod expr; pub mod expr;
@ -240,7 +99,6 @@ pub mod intern;
pub mod memory; pub mod memory;
pub mod module; pub mod module;
pub mod phantom_const; pub mod phantom_const;
pub mod platform;
pub mod prelude; pub mod prelude;
pub mod reg; pub mod reg;
pub mod reset; pub mod reset;
@ -249,5 +107,4 @@ pub mod source_location;
pub mod testing; pub mod testing;
pub mod ty; pub mod ty;
pub mod util; pub mod util;
pub mod vendor;
pub mod wire; pub mod wire;

View file

@ -7,10 +7,7 @@ use crate::{
array::{Array, ArrayType}, array::{Array, ArrayType},
bundle::{Bundle, BundleType}, bundle::{Bundle, BundleType},
clock::Clock, clock::Clock,
expr::{ expr::{Expr, Flow, ToExpr, ToLiteralBits, ops::BundleLiteral, repeat},
Expr, Flow, ToExpr, ToLiteralBits, ValueType, ops::BundleLiteral, repeat,
value_category::ValueCategoryExpr,
},
hdl, hdl,
int::{Bool, DynSize, Size, UInt, UIntType}, int::{Bool, DynSize, Size, UInt, UIntType},
intern::{Intern, Interned}, intern::{Intern, Interned},
@ -369,16 +366,10 @@ impl<T: PortType> fmt::Debug for MemPort<T> {
} }
} }
impl<T: PortType> ValueType for MemPort<T> { impl<T: PortType> MemPort<T> {
type Type = T::Port; pub fn ty(&self) -> T::Port {
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> T::Port {
T::port_ty(self) T::port_ty(self)
} }
}
impl<T: PortType> MemPort<T> {
pub fn source_location(&self) -> SourceLocation { pub fn source_location(&self) -> SourceLocation {
self.source_location self.source_location
} }
@ -839,7 +830,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
depth: Option<usize>, depth: Option<usize>,
initial_value: Expr<Array>, initial_value: Expr<Array>,
) -> Interned<BitSlice> { ) -> Interned<BitSlice> {
let initial_value_ty = initial_value.ty(); let initial_value_ty = Expr::ty(initial_value);
assert_eq!( assert_eq!(
*mem_element_type, *mem_element_type,
Element::from_canonical(initial_value_ty.element()), Element::from_canonical(initial_value_ty.element()),
@ -1020,7 +1011,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
target.depth, target.depth,
initial_value, initial_value,
)); ));
target.depth = Some(initial_value.ty().len()); target.depth = Some(Expr::ty(initial_value).len());
} }
#[track_caller] #[track_caller]
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) { pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {
@ -1093,7 +1084,6 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
.to_expr(), .to_expr(),
)), )),
CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())), CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())),
CanonicalType::TraceAsString(ty) => Expr::from_canonical(splat_mask(ty.inner_ty(), value)),
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
pub mod deduce_resets; pub mod deduce_resets;
pub mod deduce_structural_eq_flags;
pub mod simplify_enums; pub mod simplify_enums;
pub mod simplify_memories; pub mod simplify_memories;
pub mod visit; pub mod visit;

View file

@ -6,12 +6,11 @@ use crate::{
bundle::{BundleField, BundleType}, bundle::{BundleField, BundleType},
enum_::{EnumType, EnumVariant}, enum_::{EnumType, EnumVariant},
expr::{ expr::{
ExprEnum, ValueType, ExprEnum,
ops::{self, ArrayLiteral}, ops::{self, ArrayLiteral},
target::{ target::{
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement, TargetPathToTraceAsString, TargetPathDynArrayElement, TargetPathElement,
TargetPathTraceAsStringInner,
}, },
}, },
formal::FormalKind, formal::FormalKind,
@ -27,7 +26,6 @@ use crate::{
prelude::*, prelude::*,
reset::{ResetType, ResetTypeDispatch}, reset::{ResetType, ResetTypeDispatch},
sim::ExternModuleSimulation, sim::ExternModuleSimulation,
ty::TraceAsString,
util::{HashMap, HashSet}, util::{HashMap, HashSet},
}; };
use hashbrown::hash_map::Entry; use hashbrown::hash_map::Entry;
@ -105,10 +103,6 @@ enum ResetsLayout {
element: Interned<ResetsLayout>, element: Interned<ResetsLayout>,
reset_count: usize, reset_count: usize,
}, },
Transparent {
inner: Interned<ResetsLayout>,
reset_count: usize,
},
} }
impl ResetsLayout { impl ResetsLayout {
@ -118,8 +112,7 @@ impl ResetsLayout {
ResetsLayout::Reset | ResetsLayout::SyncReset | ResetsLayout::AsyncReset => 1, ResetsLayout::Reset | ResetsLayout::SyncReset | ResetsLayout::AsyncReset => 1,
ResetsLayout::Bundle { reset_count, .. } ResetsLayout::Bundle { reset_count, .. }
| ResetsLayout::Enum { reset_count, .. } | ResetsLayout::Enum { reset_count, .. }
| ResetsLayout::Array { reset_count, .. } | ResetsLayout::Array { reset_count, .. } => reset_count,
| ResetsLayout::Transparent { reset_count, .. } => reset_count,
} }
} }
fn new(ty: CanonicalType) -> Self { fn new(ty: CanonicalType) -> Self {
@ -173,13 +166,6 @@ impl ResetsLayout {
CanonicalType::Clock(_) => ResetsLayout::NoResets, CanonicalType::Clock(_) => ResetsLayout::NoResets,
CanonicalType::PhantomConst(_) => ResetsLayout::NoResets, CanonicalType::PhantomConst(_) => ResetsLayout::NoResets,
CanonicalType::DynSimOnly(_) => ResetsLayout::NoResets, CanonicalType::DynSimOnly(_) => ResetsLayout::NoResets,
CanonicalType::TraceAsString(ty) => {
let inner = ResetsLayout::new(ty.inner_ty()).intern_sized();
ResetsLayout::Transparent {
inner,
reset_count: inner.reset_count(),
}
}
} }
} }
} }
@ -329,12 +315,6 @@ impl ResetGraph {
} => { } => {
self.append_new_nodes_for_layout(*element, node_indexes, source_location); self.append_new_nodes_for_layout(*element, node_indexes, source_location);
} }
ResetsLayout::Transparent {
inner,
reset_count: _,
} => {
self.append_new_nodes_for_layout(*inner, node_indexes, source_location);
}
} }
} }
} }
@ -377,21 +357,6 @@ impl Resets {
node_indexes: self.node_indexes, node_indexes: self.node_indexes,
} }
} }
fn trace_as_string_inner(self) -> Self {
let trace_as_string = TraceAsString::from_canonical(self.ty);
let ResetsLayout::Transparent {
inner,
reset_count: _,
} = self.layout
else {
unreachable!();
};
Self {
ty: trace_as_string.inner_ty(),
layout: *inner,
node_indexes: self.node_indexes,
}
}
fn bundle_fields(self) -> impl Iterator<Item = Self> { fn bundle_fields(self) -> impl Iterator<Item = Self> {
let bundle = Bundle::from_canonical(self.ty); let bundle = Bundle::from_canonical(self.ty);
let ResetsLayout::Bundle { let ResetsLayout::Bundle {
@ -515,17 +480,6 @@ impl Resets {
CanonicalType::SyncReset(SyncReset) CanonicalType::SyncReset(SyncReset)
}, },
), ),
CanonicalType::TraceAsString(ty) => Ok(CanonicalType::TraceAsString(
ty.with_new_inner_ty(
self.array_elements()
.substituted_type(
reset_graph,
fallback_to_sync_reset,
fallback_error_source_location,
)?
.intern_sized(),
),
)),
} }
} }
} }
@ -567,7 +521,7 @@ impl State {
Entry::Vacant(entry) => ( Entry::Vacant(entry) => (
*entry.insert(Resets::with_new_nodes( *entry.insert(Resets::with_new_nodes(
&mut self.reset_graph, &mut self.reset_graph,
expr.ty(), Expr::ty(expr),
source_location, source_location,
)), )),
true, true,
@ -1059,20 +1013,18 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
| CanonicalType::Bundle(_) | CanonicalType::Bundle(_)
| CanonicalType::Reset(_) | CanonicalType::Reset(_)
| CanonicalType::PhantomConst(_) | CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) | CanonicalType::DynSimOnly(_) => unreachable!(),
| CanonicalType::TraceAsString(_) => unreachable!(),
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)* $(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
} }
}; };
} }
macro_rules! match_arg_ty { macro_rules! match_arg_ty {
($($Variant:ident),*) => { ($($Variant:ident),*) => {
*match arg.ty() { *match Expr::ty(arg) {
CanonicalType::Array(_) CanonicalType::Array(_)
| CanonicalType::Enum(_) | CanonicalType::Enum(_)
| CanonicalType::Bundle(_) | CanonicalType::Bundle(_)
| CanonicalType::Reset(_) | CanonicalType::Reset(_) => unreachable!(),
| CanonicalType::TraceAsString(_) => unreachable!(),
CanonicalType::PhantomConst(_) | CanonicalType::PhantomConst(_) |
CanonicalType::DynSimOnly(_) => Expr::expr_enum(arg), CanonicalType::DynSimOnly(_) => Expr::expr_enum(arg),
$(CanonicalType::$Variant(_) => { $(CanonicalType::$Variant(_) => {
@ -1204,11 +1156,6 @@ impl<P: Pass> RunPass<P> for ExprEnum {
ExprEnum::SliceSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::SliceSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastToBits(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::CastToBits(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastBitsTo(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::CastBitsTo(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::TraceAsStringAsInner(expr) => {
Ok(expr.run_pass(pass_args)?.map(ExprEnum::from))
}
ExprEnum::ToTraceAsString(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::StructuralEq(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ModuleIO(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::ModuleIO(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::Instance(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::Instance(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::Wire(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::Wire(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
@ -1216,10 +1163,6 @@ impl<P: Pass> RunPass<P> for ExprEnum {
ExprEnum::RegSync(expr) => reg_expr_run_pass(expr, pass_args), ExprEnum::RegSync(expr) => reg_expr_run_pass(expr, pass_args),
ExprEnum::RegAsync(expr) => reg_expr_run_pass(expr, pass_args), ExprEnum::RegAsync(expr) => reg_expr_run_pass(expr, pass_args),
ExprEnum::MemPort(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::MemPort(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::FormalInput(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::SimIoForGlobal(_) => {
unreachable!("Module is known to not contain SimIoForGlobal from validation")
}
} }
} }
} }
@ -1593,96 +1536,6 @@ impl RunPassExpr for ops::CastBitsTo {
} }
} }
impl RunPassExpr for ops::TraceAsStringAsInner {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.arg())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(resets, args_resets[0].trace_as_string_inner(), None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(Expr::from_canonical(new_args[0])))
}
}
impl RunPassExpr for ops::ToTraceAsString {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.inner())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(resets.trace_as_string_inner(), args_resets[0], None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(
new_args[0],
self.ty().with_new_inner_ty(new_args[0].ty().intern_sized()),
))
}
}
impl RunPassExpr for ops::StructuralEq {
type Args<'a> = [Expr<CanonicalType>; 2];
fn args<'a>(&'a self) -> Self::Args<'a> {
[self.lhs(), self.rhs()]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
_resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(args_resets[0], args_resets[1], None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::with_flags(new_args[0], new_args[1], self.flags()))
}
}
impl RunPassExpr for ModuleIO<CanonicalType> { impl RunPassExpr for ModuleIO<CanonicalType> {
type Args<'a> = [Expr<CanonicalType>; 0]; type Args<'a> = [Expr<CanonicalType>; 0];
@ -1807,8 +1660,7 @@ impl RunPassDispatch for AnyReg {
let clock_domain = Expr::<Bundle>::from_canonical( let clock_domain = Expr::<Bundle>::from_canonical(
Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0, Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0,
); );
match clock_domain match Expr::ty(clock_domain)
.ty()
.field_by_name("rst".intern()) .field_by_name("rst".intern())
.expect("ClockDomain has rst field") .expect("ClockDomain has rst field")
.ty .ty
@ -1838,8 +1690,7 @@ impl RunPassDispatch for AnyReg {
| CanonicalType::Reset(_) | CanonicalType::Reset(_)
| CanonicalType::Clock(_) | CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_) | CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) | CanonicalType::DynSimOnly(_) => unreachable!(),
| CanonicalType::TraceAsString(_) => unreachable!(),
} }
}) })
} }
@ -1951,7 +1802,6 @@ impl_run_pass_clone!([] ExternModuleParameter);
impl_run_pass_clone!([] SIntValue); impl_run_pass_clone!([] SIntValue);
impl_run_pass_clone!([] std::ops::Range<usize>); impl_run_pass_clone!([] std::ops::Range<usize>);
impl_run_pass_clone!([] UIntValue); impl_run_pass_clone!([] UIntValue);
impl_run_pass_clone!([] crate::vendor::xilinx::XilinxAnnotation);
impl_run_pass_copy!([] BlackBoxInlineAnnotation); impl_run_pass_copy!([] BlackBoxInlineAnnotation);
impl_run_pass_copy!([] BlackBoxPathAnnotation); impl_run_pass_copy!([] BlackBoxPathAnnotation);
impl_run_pass_copy!([] bool); impl_run_pass_copy!([] bool);
@ -1966,7 +1816,6 @@ impl_run_pass_copy!([] SVAttributeAnnotation);
impl_run_pass_copy!([] UInt); impl_run_pass_copy!([] UInt);
impl_run_pass_copy!([] usize); impl_run_pass_copy!([] usize);
impl_run_pass_copy!([] FormalKind); impl_run_pass_copy!([] FormalKind);
impl_run_pass_copy!([] crate::formal::FormalInput);
impl_run_pass_copy!([] PhantomConst); impl_run_pass_copy!([] PhantomConst);
macro_rules! impl_run_pass_for_struct { macro_rules! impl_run_pass_for_struct {
@ -2223,7 +2072,6 @@ impl_run_pass_for_struct! {
impl[] RunPass for ExternModuleBody { impl[] RunPass for ExternModuleBody {
verilog_name: _, verilog_name: _,
parameters: _, parameters: _,
clocks_for_past: _,
simulation: _, simulation: _,
} }
} }
@ -2283,12 +2131,6 @@ impl<P: Pass> RunPass<P> for TargetBase {
&TargetBase::RegAsync(v) => v.into(), &TargetBase::RegAsync(v) => v.into(),
TargetBase::Wire(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Wire)), TargetBase::Wire(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Wire)),
TargetBase::Instance(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Instance)), TargetBase::Instance(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Instance)),
TargetBase::FormalInput(v) => {
return Ok(v.run_pass(pass_args)?.map(TargetBase::FormalInput));
}
TargetBase::SimIoForGlobal(_) => {
unreachable!("Module is known to not contain SimIoForGlobal from validation")
}
}; };
Ok(reg.run_pass(pass_args)?.map(|reg| match reg { Ok(reg.run_pass(pass_args)?.map(|reg| match reg {
AnyReg::Reg(reg) => TargetBase::Reg(reg), AnyReg::Reg(reg) => TargetBase::Reg(reg),
@ -2328,6 +2170,30 @@ impl<P: Pass> RunPass<P> for StmtDeclaration {
} }
} }
impl_run_pass_for_struct! {
impl[] RunPass for TargetPathBundleField {
name: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for TargetPathArrayElement {
index: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for TargetPathDynArrayElement {}
}
impl_run_pass_for_enum! {
impl[] RunPass for TargetPathElement {
BundleField(v),
ArrayElement(v),
DynArrayElement(v),
}
}
impl_run_pass_for_enum! { impl_run_pass_for_enum! {
impl[] RunPass for Target { impl[] RunPass for Target {
Base(v), Base(v),
@ -2335,28 +2201,11 @@ impl_run_pass_for_enum! {
} }
} }
impl<P: Pass> RunPass<P> for TargetChild { impl_run_pass_for_struct! {
fn run_pass( #[constructor = TargetChild::new(parent, path_element)]
&self, impl[] RunPass for TargetChild {
mut pass_args: PassArgs<'_, P>, parent(): _,
) -> Result<PassOutput<Self, P>, DeduceResetsError> { path_element(): _,
Ok(self.parent().run_pass(pass_args.as_mut())?.map(|parent| {
let path_element = match *self.path_element() {
TargetPathElement::BundleField(TargetPathBundleField { name: _ })
| TargetPathElement::ArrayElement(TargetPathArrayElement { index: _ })
| TargetPathElement::DynArrayElement(TargetPathDynArrayElement {})
| TargetPathElement::TraceAsStringInner(TargetPathTraceAsStringInner {}) => {
self.path_element()
}
TargetPathElement::ToTraceAsString(TargetPathToTraceAsString { ty }) => {
TargetPathElement::from(TargetPathToTraceAsString {
ty: ty.with_new_inner_ty(parent.canonical_ty().intern_sized()),
})
.intern_sized()
}
};
TargetChild::new(parent, path_element)
}))
} }
} }
@ -2368,7 +2217,6 @@ impl_run_pass_for_enum! {
BlackBoxPath(v), BlackBoxPath(v),
DocString(v), DocString(v),
CustomFirrtl(v), CustomFirrtl(v),
Xilinx(v),
} }
} }

View file

@ -1,26 +1,27 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
bundle::{BundleField, BundleType}, array::{Array, ArrayType},
enum_::{EnumType, EnumVariant}, bundle::{Bundle, BundleField, BundleType},
enum_::{Enum, EnumType, EnumVariant},
expr::{ expr::{
ExprEnum, CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr,
ops::{self, EnumLiteral, StructuralEq, StructuralEqFlags}, ops::{self, EnumLiteral},
}, },
intern::{Intern, InternSlice, Interned, Memoize}, hdl,
memory::{DynPortType, MemPort}, int::UInt,
intern::{Intern, Interned, Memoize},
memory::{DynPortType, Mem, MemPort},
module::{ module::{
Block, Id, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
transform::{ transform::visit::{Fold, Folder},
deduce_structural_eq_flags::deduce_structural_eq_flags,
visit::{Fold, Folder},
}, },
}, source_location::SourceLocation,
prelude::*, ty::{CanonicalType, Type},
util::HashMap, util::HashMap,
wire::Wire,
}; };
use serde::{Deserialize, Serialize}; use core::fmt;
use std::fmt;
#[derive(Debug)] #[derive(Debug)]
pub enum SimplifyEnumsError { pub enum SimplifyEnumsError {
@ -62,7 +63,6 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool {
.fields() .fields()
.iter() .iter()
.any(|field| contains_any_enum_types(field.ty)), .any(|field| contains_any_enum_types(field.ty)),
CanonicalType::TraceAsString(ty) => contains_any_enum_types(ty.inner_ty()),
CanonicalType::UInt(_) CanonicalType::UInt(_)
| CanonicalType::SInt(_) | CanonicalType::SInt(_)
| CanonicalType::Bool(_) | CanonicalType::Bool(_)
@ -94,13 +94,11 @@ enum EnumTypeState {
struct ModuleState { struct ModuleState {
module_name: NameId, module_name: NameId,
expr_cache: HashMap<ExprEnum, ExprEnum>,
source_location: SourceLocation,
} }
impl ModuleState { impl ModuleState {
fn gen_name(&mut self, name: &str) -> ScopedNameId { fn gen_name(&mut self, name: &str) -> ScopedNameId {
ScopedNameId(self.module_name.into(), NameId(name.intern(), Id::new())) ScopedNameId(self.module_name, NameId(name.intern(), Id::new()))
} }
} }
@ -109,45 +107,6 @@ struct State {
replacement_mem_ports: HashMap<MemPort<DynPortType>, Wire<CanonicalType>>, replacement_mem_ports: HashMap<MemPort<DynPortType>, Wire<CanonicalType>>,
kind: SimplifyEnumsKind, kind: SimplifyEnumsKind,
module_state_stack: Vec<ModuleState>, module_state_stack: Vec<ModuleState>,
new_prefix_stmts_for_block: Vec<Stmt>,
new_suffix_stmts_for_block: Vec<Stmt>,
}
struct BlockScope<'a> {
state: &'a mut State,
parent_new_prefix_stmts_for_block: Vec<Stmt>,
parent_new_suffix_stmts_for_block: Vec<Stmt>,
}
impl<'a> BlockScope<'a> {
fn new(
state: &'a mut State,
new_prefix_stmts_for_block: Vec<Stmt>,
new_suffix_stmts_for_block: Vec<Stmt>,
) -> Self {
let parent_new_prefix_stmts_for_block = std::mem::replace(
&mut state.new_prefix_stmts_for_block,
new_prefix_stmts_for_block,
);
let parent_new_suffix_stmts_for_block = std::mem::replace(
&mut state.new_suffix_stmts_for_block,
new_suffix_stmts_for_block,
);
Self {
state,
parent_new_prefix_stmts_for_block,
parent_new_suffix_stmts_for_block,
}
}
}
impl Drop for BlockScope<'_> {
fn drop(&mut self) {
self.state.new_prefix_stmts_for_block =
std::mem::take(&mut self.parent_new_prefix_stmts_for_block);
self.state.new_suffix_stmts_for_block =
std::mem::take(&mut self.parent_new_suffix_stmts_for_block);
}
} }
impl State { impl State {
@ -353,24 +312,6 @@ impl State {
} }
Ok(()) Ok(())
} }
fn handle_stmt_connect_trace_as_string(
&mut self,
unfolded_lhs_ty: TraceAsString,
unfolded_rhs_ty: TraceAsString,
folded_lhs: Expr<TraceAsString>,
folded_rhs: Expr<TraceAsString>,
source_location: SourceLocation,
output_stmts: &mut Vec<Stmt>,
) -> Result<(), SimplifyEnumsError> {
self.handle_stmt_connect(
unfolded_lhs_ty.inner_ty(),
unfolded_rhs_ty.inner_ty(),
ops::TraceAsStringAsInner::new(folded_lhs).to_expr(),
ops::TraceAsStringAsInner::new(folded_rhs).to_expr(),
source_location,
output_stmts,
)
}
fn handle_stmt_connect_bundle( fn handle_stmt_connect_bundle(
&mut self, &mut self,
unfolded_lhs_ty: Bundle, unfolded_lhs_ty: Bundle,
@ -567,15 +508,6 @@ impl State {
source_location, source_location,
output_stmts, output_stmts,
), ),
CanonicalType::TraceAsString(unfolded_lhs_ty) => self
.handle_stmt_connect_trace_as_string(
unfolded_lhs_ty,
TraceAsString::from_canonical(unfolded_rhs_ty),
Expr::from_canonical(folded_lhs),
Expr::from_canonical(folded_rhs),
source_location,
output_stmts,
),
CanonicalType::UInt(_) CanonicalType::UInt(_)
| CanonicalType::SInt(_) | CanonicalType::SInt(_)
| CanonicalType::Bool(_) | CanonicalType::Bool(_)
@ -587,185 +519,6 @@ impl State {
| CanonicalType::DynSimOnly(_) => unreachable!(), | CanonicalType::DynSimOnly(_) => unreachable!(),
} }
} }
fn handle_enum_structural_eq(
&mut self,
unfolded_ty: Enum,
folded_lhs: Expr<CanonicalType>,
folded_rhs: Expr<CanonicalType>,
flags: StructuralEqFlags,
) -> Result<Expr<Bool>, SimplifyEnumsError> {
if flags.assume_padding_is_zeroed {
return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr());
}
let enum_type_state = self.get_or_make_enum_type_state(unfolded_ty)?;
if let EnumTypeState::Unchanged = enum_type_state {
return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr());
}
let module_state = self.module_state_stack.last_mut().unwrap();
let source_location = module_state.source_location;
let output_wire = Wire::new_unchecked(
module_state.gen_name("__enum_structural_eq"),
source_location,
Bool,
);
self.new_prefix_stmts_for_block.push(
StmtWire {
annotations: Interned::default(),
wire: output_wire.canonical(),
}
.into(),
);
let output_wire = output_wire.to_expr();
self.new_suffix_stmts_for_block.push(
StmtConnect {
lhs: Expr::canonical(output_wire),
rhs: Expr::canonical(false.to_expr()),
source_location,
}
.into(),
);
let tags_eq = match enum_type_state {
EnumTypeState::TagEnumAndBody(_) => StructuralEq::with_flags(
Expr::canonical(Expr::<TagAndBody<Enum, UInt>>::from_canonical(folded_lhs).tag),
Expr::canonical(Expr::<TagAndBody<Enum, UInt>>::from_canonical(folded_rhs).tag),
StructuralEqFlags {
assume_padding_is_zeroed: true,
},
)
.to_expr(),
EnumTypeState::TagUIntAndBody(_) => {
let lhs = Expr::<TagAndBody<UInt, UInt>>::from_canonical(folded_lhs).tag;
let rhs = Expr::<TagAndBody<UInt, UInt>>::from_canonical(folded_rhs).tag;
lhs.cmp_eq(rhs)
}
EnumTypeState::UInt(_) => {
let lhs_int_tag_expr = Expr::<UInt>::from_canonical(folded_lhs)
[..unfolded_ty.discriminant_bit_width()];
let rhs_int_tag_expr = Expr::<UInt>::from_canonical(folded_rhs)
[..unfolded_ty.discriminant_bit_width()];
lhs_int_tag_expr.cmp_eq(rhs_int_tag_expr)
}
EnumTypeState::Unchanged => unreachable!(),
};
let mut match_arms = Vec::with_capacity(unfolded_ty.variants().len());
for (variant_index, variant) in unfolded_ty.variants().iter().enumerate() {
let block_scope = BlockScope::new(self, vec![], vec![]);
let this = &mut *block_scope.state;
let eq = if let Some(variant_ty) = variant.ty {
let folded_lhs =
this.handle_variant_access(unfolded_ty, folded_lhs, variant_index)?;
let folded_rhs =
this.handle_variant_access(unfolded_ty, folded_rhs, variant_index)?;
this.handle_structural_eq(variant_ty, folded_lhs, folded_rhs, flags)?
} else {
true.to_expr()
};
match_arms.push(Block {
memories: [].intern_slice(),
stmts: this
.new_prefix_stmts_for_block
.drain(..)
.chain([StmtConnect {
lhs: Expr::canonical(output_wire),
rhs: Expr::canonical(eq),
source_location,
}
.into()])
.chain(this.new_suffix_stmts_for_block.drain(..))
.collect(),
});
}
let match_stmt =
self.handle_match(unfolded_ty, folded_lhs, source_location, &match_arms)?;
self.new_suffix_stmts_for_block.push(
StmtIf {
cond: tags_eq,
source_location,
blocks: [
Block {
memories: [].intern_slice(),
stmts: [match_stmt].intern_slice(),
},
Block {
memories: [].intern_slice(),
stmts: [].intern_slice(),
},
],
}
.into(),
);
Ok(output_wire)
}
fn handle_structural_eq(
&mut self,
unfolded_ty: CanonicalType,
folded_lhs: Expr<CanonicalType>,
folded_rhs: Expr<CanonicalType>,
flags: StructuralEqFlags,
) -> Result<Expr<Bool>, SimplifyEnumsError> {
if !contains_any_enum_types(unfolded_ty) {
return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr());
}
match unfolded_ty {
CanonicalType::Array(unfolded_ty) => {
let unfolded_element_ty = unfolded_ty.element();
let mut retval = None;
for i in 0..unfolded_ty.len() {
let element_eq = self.handle_structural_eq(
unfolded_element_ty,
ops::ArrayIndex::new(Expr::from_canonical(folded_lhs), i).to_expr(),
ops::ArrayIndex::new(Expr::from_canonical(folded_rhs), i).to_expr(),
flags,
)?;
retval = Some(match retval {
Some(old_eq) => old_eq & element_eq,
None => element_eq,
});
}
Ok(retval.unwrap_or_else(|| {
StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr()
}))
}
CanonicalType::Enum(unfolded_ty) => {
self.handle_enum_structural_eq(unfolded_ty, folded_lhs, folded_rhs, flags)
}
CanonicalType::Bundle(unfolded_ty) => {
let mut retval = None;
for (i, field) in unfolded_ty.fields().iter().enumerate() {
let field_eq = self.handle_structural_eq(
field.ty,
ops::FieldAccess::new_by_index(Expr::from_canonical(folded_lhs), i)
.to_expr(),
ops::FieldAccess::new_by_index(Expr::from_canonical(folded_rhs), i)
.to_expr(),
flags,
)?;
retval = Some(match retval {
Some(old_eq) => old_eq & field_eq,
None => field_eq,
});
}
Ok(retval.unwrap_or_else(|| {
StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr()
}))
}
CanonicalType::TraceAsString(unfolded_ty) => self.handle_structural_eq(
unfolded_ty.inner_ty(),
*Expr::<TraceAsString>::from_canonical(folded_lhs),
*Expr::<TraceAsString>::from_canonical(folded_rhs),
flags,
),
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => unreachable!("doesn't contain any enum types"),
}
}
} }
fn connect_port( fn connect_port(
@ -774,9 +527,7 @@ fn connect_port(
rhs: Expr<CanonicalType>, rhs: Expr<CanonicalType>,
source_location: SourceLocation, source_location: SourceLocation,
) { ) {
let lhs = Expr::unwrap_transparent_types(lhs); if Expr::ty(lhs) == Expr::ty(rhs) {
let rhs = Expr::unwrap_transparent_types(rhs);
if lhs.ty() == rhs.ty() {
stmts.push( stmts.push(
StmtConnect { StmtConnect {
lhs, lhs,
@ -787,7 +538,7 @@ fn connect_port(
); );
return; return;
} }
match (lhs.ty(), rhs.ty()) { match (Expr::ty(lhs), Expr::ty(rhs)) {
(CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => { (CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => {
let lhs = Expr::<Bundle>::from_canonical(lhs); let lhs = Expr::<Bundle>::from_canonical(lhs);
for field in lhs_type.fields() { for field in lhs_type.fields() {
@ -821,9 +572,6 @@ fn connect_port(
connect_port(stmts, lhs[index], rhs[index], source_location); connect_port(stmts, lhs[index], rhs[index], source_location);
} }
} }
(CanonicalType::TraceAsString(_), CanonicalType::TraceAsString(_)) => {
unreachable!("handled by unwrap_transparent_types")
}
(CanonicalType::Bundle(_), _) (CanonicalType::Bundle(_), _)
| (CanonicalType::Enum(_), _) | (CanonicalType::Enum(_), _)
| (CanonicalType::Array(_), _) | (CanonicalType::Array(_), _)
@ -835,11 +583,10 @@ fn connect_port(
| (CanonicalType::SyncReset(_), _) | (CanonicalType::SyncReset(_), _)
| (CanonicalType::Reset(_), _) | (CanonicalType::Reset(_), _)
| (CanonicalType::PhantomConst(_), _) | (CanonicalType::PhantomConst(_), _)
| (CanonicalType::DynSimOnly(_), _) | (CanonicalType::DynSimOnly(_), _) => unreachable!(
| (CanonicalType::TraceAsString(_), _) => unreachable!(
"trying to connect memory ports:\n{:?}\n{:?}", "trying to connect memory ports:\n{:?}\n{:?}",
lhs.ty(), Expr::ty(lhs),
rhs.ty(), Expr::ty(rhs),
), ),
} }
} }
@ -859,23 +606,20 @@ fn match_int_tag(
}; };
}; };
let mut retval = StmtIf { let mut retval = StmtIf {
cond: int_tag_expr.cmp_eq( cond: int_tag_expr
int_tag_expr .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)),
.ty()
.from_int_wrapping(next_to_last_variant_index),
),
source_location, source_location,
blocks: [next_to_last_block, last_block], blocks: [next_to_last_block, last_block],
}; };
for (variant_index, block) in blocks_iter.rev() { for (variant_index, block) in blocks_iter.rev() {
retval = StmtIf { retval = StmtIf {
cond: int_tag_expr.cmp_eq(int_tag_expr.ty().from_int_wrapping(variant_index)), cond: int_tag_expr.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)),
source_location, source_location,
blocks: [ blocks: [
block, block,
Block { Block {
memories: Default::default(), memories: Default::default(),
stmts: [Stmt::from(retval)].intern_slice(), stmts: [Stmt::from(retval)][..].intern(),
}, },
], ],
}; };
@ -893,8 +637,6 @@ impl Folder for State {
fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> { fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> {
self.module_state_stack.push(ModuleState { self.module_state_stack.push(ModuleState {
module_name: v.name_id(), module_name: v.name_id(),
expr_cache: HashMap::default(),
source_location: v.source_location(),
}); });
let retval = Fold::default_fold(v, self); let retval = Fold::default_fold(v, self);
self.module_state_stack.pop(); self.module_state_stack.pop();
@ -902,51 +644,30 @@ impl Folder for State {
} }
fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> { fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> {
if let Some(folded_op) = self match op {
.module_state_stack
.last()
.expect("known to be in module")
.expr_cache
.get(&op)
{
return Ok(*folded_op);
}
let folded_op = match op {
ExprEnum::EnumLiteral(op) => { ExprEnum::EnumLiteral(op) => {
let folded_variant_value = op.variant_value().map(|v| v.fold(self)).transpose()?; let folded_variant_value = op.variant_value().map(|v| v.fold(self)).transpose()?;
*Expr::expr_enum(self.handle_enum_literal( Ok(*Expr::expr_enum(self.handle_enum_literal(
op.ty(), op.ty(),
op.variant_index(), op.variant_index(),
folded_variant_value, folded_variant_value,
)?) )?))
} }
ExprEnum::VariantAccess(op) => { ExprEnum::VariantAccess(op) => {
let folded_base_expr = Expr::canonical(op.base()).fold(self)?; let folded_base_expr = Expr::canonical(op.base()).fold(self)?;
*Expr::expr_enum(self.handle_variant_access( Ok(*Expr::expr_enum(self.handle_variant_access(
op.base().ty(), Expr::ty(op.base()),
folded_base_expr, folded_base_expr,
op.variant_index(), op.variant_index(),
)?) )?))
} }
ExprEnum::StructuralEq(op) => { ExprEnum::MemPort(mem_port) => Ok(
let ty = op.lhs().ty();
assert_eq!(ty, op.rhs().ty());
let folded_lhs = Expr::canonical(op.lhs()).fold(self)?;
let folded_rhs = Expr::canonical(op.rhs()).fold(self)?;
*Expr::expr_enum(self.handle_structural_eq(
ty,
folded_lhs,
folded_rhs,
op.flags(),
)?)
}
ExprEnum::MemPort(mem_port) => {
if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) { if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) {
ExprEnum::Wire(wire) ExprEnum::Wire(wire)
} else { } else {
ExprEnum::MemPort(mem_port.fold(self)?) ExprEnum::MemPort(mem_port.fold(self)?)
} },
} ),
ExprEnum::UIntLiteral(_) ExprEnum::UIntLiteral(_)
| ExprEnum::SIntLiteral(_) | ExprEnum::SIntLiteral(_)
| ExprEnum::BoolLiteral(_) | ExprEnum::BoolLiteral(_)
@ -1047,33 +768,21 @@ impl Folder for State {
| ExprEnum::SliceSInt(_) | ExprEnum::SliceSInt(_)
| ExprEnum::CastToBits(_) | ExprEnum::CastToBits(_)
| ExprEnum::CastBitsTo(_) | ExprEnum::CastBitsTo(_)
| ExprEnum::TraceAsStringAsInner(_)
| ExprEnum::ToTraceAsString(_)
| ExprEnum::ModuleIO(_) | ExprEnum::ModuleIO(_)
| ExprEnum::Instance(_) | ExprEnum::Instance(_)
| ExprEnum::Wire(_) | ExprEnum::Wire(_)
| ExprEnum::Reg(_) | ExprEnum::Reg(_)
| ExprEnum::RegSync(_) | ExprEnum::RegSync(_)
| ExprEnum::RegAsync(_) | ExprEnum::RegAsync(_) => op.default_fold(self),
| ExprEnum::FormalInput(_) }
| ExprEnum::SimIoForGlobal(_) => op.default_fold(self)?,
};
self.module_state_stack
.last_mut()
.expect("known to be in module")
.expr_cache
.insert(op, folded_op);
Ok(folded_op)
} }
fn fold_block(&mut self, block: Block) -> Result<Block, Self::Error> { fn fold_block(&mut self, block: Block) -> Result<Block, Self::Error> {
let block_scope = BlockScope::new(self, vec![], vec![]);
let this = &mut *block_scope.state;
let mut memories = vec![]; let mut memories = vec![];
let mut stmts = vec![]; let mut stmts = vec![];
for memory in block.memories { for memory in block.memories {
let old_element_ty = memory.array_type().element(); let old_element_ty = memory.array_type().element();
let new_element_ty = memory.array_type().element().fold(this)?; let new_element_ty = memory.array_type().element().fold(self)?;
if new_element_ty != old_element_ty { if new_element_ty != old_element_ty {
let mut new_ports = vec![]; let mut new_ports = vec![];
for port in memory.ports() { for port in memory.ports() {
@ -1099,7 +808,7 @@ impl Folder for State {
continue; continue;
} }
let wire = Wire::new_unchecked( let wire = Wire::new_unchecked(
this.module_state_stack self.module_state_stack
.last_mut() .last_mut()
.unwrap() .unwrap()
.gen_name(&format!( .gen_name(&format!(
@ -1123,7 +832,7 @@ impl Folder for State {
Expr::canonical(wire.to_expr()), Expr::canonical(wire.to_expr()),
port.source_location(), port.source_location(),
); );
this.replacement_mem_ports.insert(port, wire.canonical()); self.replacement_mem_ports.insert(port, wire.canonical());
} }
memories.push(Mem::new_unchecked( memories.push(Mem::new_unchecked(
memory.scoped_name(), memory.scoped_name(),
@ -1138,12 +847,10 @@ impl Folder for State {
memory.mem_annotations(), memory.mem_annotations(),
)); ));
} else { } else {
memories.push(memory.fold(this)?); memories.push(memory.fold(self)?);
} }
} }
stmts.extend_from_slice(&block.stmts.fold(this)?); stmts.extend_from_slice(&block.stmts.fold(self)?);
stmts.splice(0..0, this.new_prefix_stmts_for_block.drain(..));
stmts.extend_from_slice(&this.new_suffix_stmts_for_block);
Ok(Block { Ok(Block {
memories: Intern::intern_owned(memories), memories: Intern::intern_owned(memories),
stmts: Intern::intern_owned(stmts), stmts: Intern::intern_owned(stmts),
@ -1159,7 +866,7 @@ impl Folder for State {
}) => { }) => {
let folded_expr = Expr::canonical(expr).fold(self)?; let folded_expr = Expr::canonical(expr).fold(self)?;
let folded_blocks = blocks.fold(self)?; let folded_blocks = blocks.fold(self)?;
self.handle_match(expr.ty(), folded_expr, source_location, &folded_blocks) self.handle_match(Expr::ty(expr), folded_expr, source_location, &folded_blocks)
} }
Stmt::Connect(StmtConnect { Stmt::Connect(StmtConnect {
lhs, lhs,
@ -1170,8 +877,8 @@ impl Folder for State {
let folded_rhs = rhs.fold(self)?; let folded_rhs = rhs.fold(self)?;
let mut output_stmts = vec![]; let mut output_stmts = vec![];
self.handle_stmt_connect( self.handle_stmt_connect(
lhs.ty(), Expr::ty(lhs),
rhs.ty(), Expr::ty(rhs),
folded_lhs, folded_lhs,
folded_rhs, folded_rhs,
source_location, source_location,
@ -1225,8 +932,7 @@ impl Folder for State {
| CanonicalType::SyncReset(_) | CanonicalType::SyncReset(_)
| CanonicalType::Reset(_) | CanonicalType::Reset(_)
| CanonicalType::PhantomConst(_) | CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) | CanonicalType::DynSimOnly(_) => canonical_type.default_fold(self),
| CanonicalType::TraceAsString(_) => canonical_type.default_fold(self),
} }
} }
@ -1249,15 +955,12 @@ impl Folder for State {
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum, Serialize, Deserialize)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum)]
#[serde(rename_all = "kebab-case")]
pub enum SimplifyEnumsKind { pub enum SimplifyEnumsKind {
SimplifyToEnumsWithNoBody, SimplifyToEnumsWithNoBody,
#[clap(name = "replace-with-bundle-of-uints")] #[clap(name = "replace-with-bundle-of-uints")]
#[serde(rename = "replace-with-bundle-of-uints")]
ReplaceWithBundleOfUInts, ReplaceWithBundleOfUInts,
#[clap(name = "replace-with-uint")] #[clap(name = "replace-with-uint")]
#[serde(rename = "replace-with-uint")]
ReplaceWithUInt, ReplaceWithUInt,
} }
@ -1265,13 +968,10 @@ pub fn simplify_enums(
module: Interned<Module<Bundle>>, module: Interned<Module<Bundle>>,
kind: SimplifyEnumsKind, kind: SimplifyEnumsKind,
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> { ) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
let module = deduce_structural_eq_flags(module);
module.fold(&mut State { module.fold(&mut State {
enum_types: HashMap::default(), enum_types: HashMap::default(),
replacement_mem_ports: HashMap::default(), replacement_mem_ports: HashMap::default(),
kind, kind,
module_state_stack: vec![], module_state_stack: vec![],
new_prefix_stmts_for_block: vec![],
new_suffix_stmts_for_block: vec![],
}) })
} }

View file

@ -4,7 +4,7 @@ use crate::{
annotations::TargetedAnnotation, annotations::TargetedAnnotation,
array::Array, array::Array,
bundle::{Bundle, BundleType}, bundle::{Bundle, BundleType},
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr, ValueType}, expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr},
int::{Bool, SInt, Size, UInt}, int::{Bool, SInt, Size, UInt},
intern::{Intern, Interned}, intern::{Intern, Interned},
memory::{Mem, MemPort, PortType}, memory::{Mem, MemPort, PortType},
@ -90,7 +90,7 @@ impl MemSplit {
} }
} }
fn new(element_type: CanonicalType) -> Self { fn new(element_type: CanonicalType) -> Self {
match element_type.unwrap_transparent_types() { match element_type {
CanonicalType::Bundle(bundle_ty) => MemSplit::Bundle { CanonicalType::Bundle(bundle_ty) => MemSplit::Bundle {
fields: bundle_ty fields: bundle_ty
.fields() .fields()
@ -195,7 +195,6 @@ impl MemSplit {
| CanonicalType::SyncReset(_) | CanonicalType::SyncReset(_)
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"), | CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"), CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"),
CanonicalType::TraceAsString(_) => unreachable!("handled by unwrap_transparent_types"),
} }
} }
} }
@ -307,9 +306,7 @@ impl SplitMemState<'_, '_> {
let outer_mem_name_path_len = self.mem_name_path.len(); let outer_mem_name_path_len = self.mem_name_path.len();
match self.split { match self.split {
MemSplit::Bundle { fields } => { MemSplit::Bundle { fields } => {
let CanonicalType::Bundle(bundle_type) = let CanonicalType::Bundle(bundle_type) = self.element_type else {
self.element_type.unwrap_transparent_types()
else {
unreachable!(); unreachable!();
}; };
for ((field, field_offset), split) in bundle_type for ((field, field_offset), split) in bundle_type
@ -324,10 +321,7 @@ impl SplitMemState<'_, '_> {
let field_ty_bit_width = field.ty.bit_width(); let field_ty_bit_width = field.ty.bit_width();
self.split_state_stack.push_map( self.split_state_stack.push_map(
|e: Expr<CanonicalType>| { |e: Expr<CanonicalType>| {
Expr::field( Expr::field(Expr::<Bundle>::from_canonical(e), &field.name)
Expr::<Bundle>::from_canonical(Expr::unwrap_transparent_types(e)),
&field.name,
)
}, },
|initial_value_element| { |initial_value_element| {
let Some(field_offset) = field_offset.only_bit_width() else { let Some(field_offset) = field_offset.only_bit_width() else {
@ -383,8 +377,8 @@ impl SplitMemState<'_, '_> {
}; };
self.output_stmts.push( self.output_stmts.push(
StmtConnect { StmtConnect {
lhs: Expr::unwrap_transparent_types(Expr::field(port_expr, name)), lhs: Expr::field(port_expr, name),
rhs: Expr::unwrap_transparent_types(Expr::field(wire_expr, name)), rhs: Expr::field(wire_expr, name),
source_location: port.source_location(), source_location: port.source_location(),
} }
.into(), .into(),
@ -395,8 +389,7 @@ impl SplitMemState<'_, '_> {
self.output_mems.push(new_mem); self.output_mems.push(new_mem);
} }
MemSplit::Array { elements } => { MemSplit::Array { elements } => {
let CanonicalType::Array(array_type) = self.element_type.unwrap_transparent_types() let CanonicalType::Array(array_type) = self.element_type else {
else {
unreachable!(); unreachable!();
}; };
let element_type = array_type.element(); let element_type = array_type.element();
@ -405,7 +398,7 @@ impl SplitMemState<'_, '_> {
self.mem_name_path.truncate(outer_mem_name_path_len); self.mem_name_path.truncate(outer_mem_name_path_len);
write!(self.mem_name_path, "_{index}").unwrap(); write!(self.mem_name_path, "_{index}").unwrap();
self.split_state_stack.push_map( self.split_state_stack.push_map(
|e| Expr::<Array>::from_canonical(Expr::unwrap_transparent_types(e))[index], |e| Expr::<Array>::from_canonical(e)[index],
|initial_value_element| { |initial_value_element| {
&initial_value_element[index * element_bit_width..][..element_bit_width] &initial_value_element[index * element_bit_width..][..element_bit_width]
}, },
@ -471,7 +464,7 @@ impl ModuleState {
assert_eq!(memory_element_array_range_len % input_array_type.len(), 0); assert_eq!(memory_element_array_range_len % input_array_type.len(), 0);
let chunk_size = memory_element_array_range_len / input_array_type.len(); let chunk_size = memory_element_array_range_len / input_array_type.len();
for index in 0..input_array_type.len() { for index in 0..input_array_type.len() {
let map = |e| Expr::<Array>::from_canonical(Expr::unwrap_transparent_types(e))[index]; let map = |e| Expr::<Array>::from_canonical(e)[index];
let wire_rdata = wire_rdata.map(map); let wire_rdata = wire_rdata.map(map);
let wire_wdata = wire_wdata.map(map); let wire_wdata = wire_wdata.map(map);
let wire_wmask = wire_wmask.map(map); let wire_wmask = wire_wmask.map(map);
@ -512,8 +505,8 @@ impl ModuleState {
port_read: Expr<CanonicalType>| { port_read: Expr<CanonicalType>| {
output_stmts.push( output_stmts.push(
StmtConnect { StmtConnect {
lhs: Expr::unwrap_transparent_types(wire_read), lhs: wire_read,
rhs: Expr::unwrap_transparent_types(port_read), rhs: port_read,
source_location, source_location,
} }
.into(), .into(),
@ -524,8 +517,8 @@ impl ModuleState {
port_write: Expr<CanonicalType>| { port_write: Expr<CanonicalType>| {
output_stmts.push( output_stmts.push(
StmtConnect { StmtConnect {
lhs: Expr::unwrap_transparent_types(port_write), lhs: port_write,
rhs: Expr::unwrap_transparent_types(wire_write), rhs: wire_write,
source_location, source_location,
} }
.into(), .into(),
@ -537,8 +530,7 @@ impl ModuleState {
connect_read( connect_read(
output_stmts, output_stmts,
wire_read, wire_read,
Expr::<UInt>::from_canonical(Expr::unwrap_transparent_types(port_read)) Expr::<UInt>::from_canonical(port_read).cast_bits_to(Expr::ty(wire_read)),
.cast_bits_to(wire_read.ty()),
); );
}; };
let connect_write_enum = let connect_write_enum =
@ -552,7 +544,7 @@ impl ModuleState {
); );
}; };
loop { loop {
match input_element_type.unwrap_transparent_types() { match input_element_type {
CanonicalType::Bundle(_) => { CanonicalType::Bundle(_) => {
unreachable!("bundle types are always split") unreachable!("bundle types are always split")
} }
@ -633,9 +625,6 @@ impl ModuleState {
| CanonicalType::SyncReset(_) | CanonicalType::SyncReset(_)
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"), | CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"), CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"),
CanonicalType::TraceAsString(_) => {
unreachable!("handled by unwrap_transparent_types")
}
} }
break; break;
} }

View file

@ -11,20 +11,19 @@ use crate::{
clock::Clock, clock::Clock,
enum_::{Enum, EnumType, EnumVariant}, enum_::{Enum, EnumType, EnumVariant},
expr::{ expr::{
Expr, ExprEnum, ValueType, ops, Expr, ExprEnum, ops,
target::{ target::{
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement, TargetPathToTraceAsString, TargetPathDynArrayElement, TargetPathElement,
TargetPathTraceAsStringInner,
}, },
}, },
formal::{FormalInput, FormalInputKind, FormalKind}, formal::FormalKind,
int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue}, int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue},
intern::{Intern, Interned}, intern::{Intern, Interned},
memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite}, memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
module::{ module::{
AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter, AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter,
ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId, NameIdOrGlobal, ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId,
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf,
StmtInstance, StmtMatch, StmtReg, StmtWire, StmtInstance, StmtMatch, StmtReg, StmtWire,
}, },
@ -33,10 +32,7 @@ use crate::{
reset::{AsyncReset, Reset, ResetType, SyncReset}, reset::{AsyncReset, Reset, ResetType, SyncReset},
sim::{ExternModuleSimulation, value::DynSimOnly}, sim::{ExternModuleSimulation, value::DynSimOnly},
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, TraceAsString, Type}, ty::{CanonicalType, Type},
vendor::xilinx::{
XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation,
},
wire::Wire, wire::Wire,
}; };
use num_bigint::{BigInt, BigUint}; use num_bigint::{BigInt, BigUint};
@ -482,30 +478,4 @@ impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ mut
} }
} }
impl<State: ?Sized + Visitor> Visit<State> for NameIdOrGlobal {
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
state.visit_name_id_or_global(self)
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
match self {
Self::Global => Ok(()),
Self::NameId(name_id) => name_id.visit(state),
}
}
}
impl<State: ?Sized + Folder> Fold<State> for NameIdOrGlobal {
fn fold(self, state: &mut State) -> Result<Self, <State>::Error> {
state.fold_name_id_or_global(self)
}
fn default_fold(self, state: &mut State) -> Result<Self, <State>::Error> {
match self {
Self::Global => Ok(Self::Global),
Self::NameId(name_id) => Ok(Self::NameId(name_id.fold(state)?)),
}
}
}
include!(concat!(env!("OUT_DIR"), "/visit.rs")); include!(concat!(env!("OUT_DIR"), "/visit.rs"));

View file

@ -2,14 +2,17 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{Expr, HdlPartialEqImpl, HdlPartialOrdImpl, ToExpr, ValueType}, expr::{
Expr, ToExpr,
ops::{ExprPartialEq, ExprPartialOrd},
},
int::Bool, int::Bool,
intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize},
sim::value::{SimValue, ToSimValue, ToSimValueWithType}, sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
SimValueDebug, StaticType, Type, TypeProperties, impl_match_variant_as_self, StaticType, Type, TypeProperties, impl_match_variant_as_self,
serde_impls::{SerdeCanonicalType, SerdePhantomConst}, serde_impls::{SerdeCanonicalType, SerdePhantomConst},
}, },
}; };
@ -19,7 +22,6 @@ use serde::{
}; };
use std::{ use std::{
any::Any, any::Any,
borrow::Cow,
fmt, fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
marker::PhantomData, marker::PhantomData,
@ -129,7 +131,7 @@ impl<T: Type + PhantomConstValue> Index<T> for PhantomConstWithoutGenerics {
type Output = PhantomConst<T>; type Output = PhantomConst<T>;
fn index(&self, value: T) -> &Self::Output { fn index(&self, value: T) -> &Self::Output {
Interned::into_inner(PhantomConst::new(&value).intern_sized()) Interned::into_inner(PhantomConst::new(value.intern()).intern_sized())
} }
} }
@ -220,37 +222,16 @@ impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T,
} }
impl<T: ?Sized + PhantomConstValue> PhantomConst<T> { impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
pub fn new_interned(value: Interned<T>) -> Self { pub fn new(value: Interned<T>) -> Self {
Self { Self {
value: LazyInterned::Interned(value), value: LazyInterned::Interned(value),
} }
} }
pub fn new_sized(value: T) -> Self pub const fn new_lazy(v: &'static dyn LazyInternedTrait<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<U: Intern + std::ops::Deref<Target = T>>(value: U) -> Self
where
T: ToOwned<Owned = U>,
{
Self::new_interned(value.intern_deref())
}
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self { Self {
value: LazyInterned::new_const::<V>(), value: LazyInterned::new_lazy(v),
} }
} }
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn get(self) -> Interned<T> { pub fn get(self) -> Interned<T> {
self.value.interned() self.value.interned()
} }
@ -264,7 +245,7 @@ impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) { if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) {
return retval; return retval;
} }
<PhantomConst>::new_interned( <PhantomConst>::new(
PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()), PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()),
) )
} }
@ -272,7 +253,7 @@ impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) { if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) {
return retval; return retval;
} }
Self::new_interned( Self::new(
PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()), PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()),
) )
} }
@ -327,15 +308,6 @@ impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<T> {
} }
} }
impl<T: ?Sized + PhantomConstValue> SimValueDebug for PhantomConst<T> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: ?Sized + PhantomConstValue> Default for PhantomConst<T> impl<T: ?Sized + PhantomConstValue> Default for PhantomConst<T>
where where
Interned<T>: Default, Interned<T>: Default,
@ -349,7 +321,9 @@ impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T>
where where
Interned<T>: Default, Interned<T>: Default,
{ {
const TYPE: Self = Self::new_const_default(); const TYPE: Self = PhantomConst {
value: LazyInterned::new_lazy(&Interned::<T>::default),
};
const MASK_TYPE: Self::MaskType = (); const MASK_TYPE: Self::MaskType = ();
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
@ -372,9 +346,7 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
match SerdeType::<T>::deserialize(deserializer)? { match SerdeType::<T>::deserialize(deserializer)? {
SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => { SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => Ok(Self::new(value)),
Ok(Self::new_interned(value))
}
ty => Err(Error::invalid_value( ty => Err(Error::invalid_value(
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
&"a PhantomConst", &"a PhantomConst",
@ -383,104 +355,50 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
} }
} }
impl<T: ?Sized + PhantomConstValue> HdlPartialEqImpl<Self> for PhantomConst<T> { impl<T: ?Sized + PhantomConstValue> ExprPartialEq<Self> for PhantomConst<T> {
const TRY_STRUCTURAL_EQ: bool = true; fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
#[track_caller]
fn cmp_value_eq(
lhs: Self,
_lhs_value: Cow<'_, Self::SimValue>,
rhs: Self,
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
) -> bool {
assert_eq!(lhs, rhs);
true
}
#[track_caller]
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(lhs.ty(), rhs.ty());
true.to_expr() true.to_expr()
} }
#[track_caller] fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
assert_eq!(lhs.ty(), rhs.ty());
false.to_expr() false.to_expr()
} }
} }
impl<T: ?Sized + PhantomConstValue> HdlPartialOrdImpl<Self> for PhantomConst<T> { impl<T: ?Sized + PhantomConstValue> ExprPartialOrd<Self> for PhantomConst<T> {
#[track_caller] fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
fn cmp_value_lt( assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
lhs: Self,
_lhs_value: Cow<'_, Self::SimValue>,
rhs: Self,
_rhs_value: Cow<'_, <Self as Type>::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<'_, <Self as Type>::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<'_, <Self as Type>::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<'_, <Self as Type>::SimValue>,
) -> bool {
assert_eq!(lhs, rhs);
true
}
#[track_caller]
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(lhs.ty(), rhs.ty());
false.to_expr() false.to_expr()
} }
#[track_caller] fn cmp_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
assert_eq!(lhs.ty(), rhs.ty());
true.to_expr() true.to_expr()
} }
#[track_caller] fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
assert_eq!(lhs.ty(), rhs.ty());
false.to_expr() false.to_expr()
} }
#[track_caller] fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
assert_eq!(lhs.ty(), rhs.ty());
true.to_expr() true.to_expr()
} }
} }
impl<T: ?Sized + PhantomConstValue> SimValuePartialEq<Self> for PhantomConst<T> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
assert_eq!(SimValue::ty(this), SimValue::ty(other));
true
}
}
impl<T: ?Sized + PhantomConstValue> ToSimValue for PhantomConst<T> { impl<T: ?Sized + PhantomConstValue> ToSimValue for PhantomConst<T> {
type Type = PhantomConst<T>;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(*self, *self) SimValue::from_value(*self, *self)
} }
@ -497,71 +415,3 @@ impl<T: ?Sized + PhantomConstValue> ToSimValueWithType<CanonicalType> for Phanto
SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self)) SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self))
} }
} }
mod sealed {
pub trait Sealed<T: ?Sized> {}
}
pub trait PhantomConstGet<T: ?Sized + PhantomConstValue>: sealed::Sealed<T> {
fn get(&self) -> Interned<T>;
}
impl<T: ?Sized + PhantomConstValue, This: ?Sized + std::ops::Deref<Target: PhantomConstGet<T>>>
sealed::Sealed<T> for This
{
}
impl<T: ?Sized + PhantomConstValue, This: ?Sized + std::ops::Deref<Target: PhantomConstGet<T>>>
PhantomConstGet<T> for This
{
fn get(&self) -> Interned<T> {
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<T> for PhantomConst<T> {
fn get(&self) -> _ {
PhantomConst::get(*self)
}
}
}
impl_phantom_const_get! {
impl PhantomConstGet<T> for Expr<PhantomConst<T>> {
fn get(&self) -> _ {
PhantomConst::get(self.ty())
}
}
}
#[doc(hidden)]
pub trait ReturnSelfUnchanged<T: ?Sized> {
type Type: ?Sized;
}
impl<This: ?Sized, T: ?Sized> ReturnSelfUnchanged<T> for This {
type Type = This;
}
#[doc(hidden)]
pub fn type_alias_phantom_const_get_helper<T: ?Sized + PhantomConstValue, R: Intern + Clone>(
param: impl PhantomConstGet<T>,
get: impl FnOnce(Interned<T>) -> R,
) -> &'static R {
Interned::into_inner(get(param.get()).intern_sized())
}

File diff suppressed because it is too large Load diff

View file

@ -1,59 +0,0 @@
// 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<f64>,
}
#[hdl(no_runtime_generics, no_static)]
pub struct ClockInput {
pub clk: Clock,
pub properties: PhantomConst<ClockInputProperties>,
}
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,
}

View file

@ -7,17 +7,17 @@ pub use crate::{
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
}, },
array::{Array, ArrayType}, array::{Array, ArrayType},
build::{BuildCli, JobParams, RunBuild},
bundle::Bundle, bundle::Bundle,
cli::Cli,
clock::{Clock, ClockDomain, ToClock}, clock::{Clock, ClockDomain, ToClock},
enum_::{Enum, HdlNone, HdlOption, HdlSome}, enum_::{Enum, HdlNone, HdlOption, HdlSome},
expr::{ expr::{
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr,
ReduceBits, ToExpr, ToTraceAsString, ValueType, repeat, ReduceBits, ToExpr, repeat,
}, },
formal::{ formal::{
all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, hdl_assert, MakeFormalExpr, all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset,
hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover, hdl_assert, hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover,
hdl_cover_with_enable, hdl_cover_with_enable,
}, },
hdl, hdl_module, hdl, hdl_module,
@ -27,8 +27,7 @@ pub use crate::{
Instance, Module, ModuleBuilder, annotate, connect, connect_any, incomplete_wire, instance, Instance, Module, ModuleBuilder, annotate, connect, connect_any, incomplete_wire, instance,
memory, memory_array, memory_with_init, reg_builder, wire, memory, memory_array, memory_with_init, reg_builder, wire,
}, },
phantom_const::{PhantomConst, PhantomConstGet}, phantom_const::PhantomConst,
platform::{DynPlatform, Platform, PlatformIOBuilder, peripherals},
reg::Reg, reg::Reg,
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
sim::{ sim::{
@ -37,8 +36,7 @@ pub use crate::{
value::{SimOnly, SimOnlyValue, SimValue, ToSimValue, ToSimValueWithType}, value::{SimOnly, SimOnlyValue, SimValue, ToSimValue, ToSimValueWithType},
}, },
source_location::SourceLocation, source_location::SourceLocation,
testing::{FormalMode, assert_formal, checked_vcd_output}, ty::{AsMask, CanonicalType, Type},
ty::{AsMask, CanonicalType, TraceAsString, Type},
util::{ConstUsize, GenericConstUsize}, util::{ConstUsize, GenericConstUsize},
wire::Wire, wire::Wire,
}; };

View file

@ -2,7 +2,7 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
clock::ClockDomain, clock::ClockDomain,
expr::{Expr, Flow, ValueType, value_category::ValueCategoryExpr}, expr::{Expr, Flow},
intern::Interned, intern::Interned,
module::{NameId, ScopedNameId}, module::{NameId, ScopedNameId},
reset::{Reset, ResetType}, reset::{Reset, ResetType},
@ -20,16 +20,7 @@ pub struct Reg<T: Type, R: ResetType = Reset> {
init: Option<Expr<T>>, init: Option<Expr<T>>,
} }
impl<T: Type, R: ResetType> ValueType for Reg<T, R> { impl<T: Type + fmt::Debug, R: ResetType> fmt::Debug for Reg<T, R> {
type Type = T;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
self.ty
}
}
impl<T: Type, R: ResetType> fmt::Debug for Reg<T, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { let Self {
name, name,
@ -77,9 +68,8 @@ impl<T: Type, R: ResetType> Reg<T, R> {
"register type must be a storable type" "register type must be a storable type"
); );
if let Some(init) = init { if let Some(init) = init {
assert_eq!(ty, init.ty(), "register's type must match init type"); assert_eq!(ty, Expr::ty(init), "register's type must match init type");
} }
scoped_name.0.assert_is_name_id();
Self { Self {
name: scoped_name, name: scoped_name,
source_location, source_location,
@ -88,6 +78,9 @@ impl<T: Type, R: ResetType> Reg<T, R> {
init, init,
} }
} }
pub fn ty(&self) -> T {
self.ty
}
pub fn source_location(&self) -> SourceLocation { pub fn source_location(&self) -> SourceLocation {
self.source_location self.source_location
} }
@ -95,7 +88,7 @@ impl<T: Type, R: ResetType> Reg<T, R> {
self.containing_module_name_id().0 self.containing_module_name_id().0
} }
pub fn containing_module_name_id(&self) -> NameId { pub fn containing_module_name_id(&self) -> NameId {
self.name.0.unwrap_name_id() self.name.0
} }
pub fn name(&self) -> Interned<str> { pub fn name(&self) -> Interned<str> {
self.name_id().0 self.name_id().0

View file

@ -1,21 +1,16 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
clock::Clock, clock::Clock,
expr::{CastToImpl, Expr, ValueType}, expr::{Expr, ToExpr, ops},
int::{Bool, SInt, SIntValue, UInt, UIntValue}, int::{Bool, SInt, UInt},
sim::value::SimValue,
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties, OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
impl_match_variant_as_self,
}, },
util::ConstUsize,
}; };
use bitvec::{bits, order::Lsb0}; use bitvec::{bits, order::Lsb0};
use std::fmt;
mod sealed { mod sealed {
pub trait ResetTypeSealed {} pub trait ResetTypeSealed {}
@ -24,15 +19,15 @@ mod sealed {
pub trait ResetType: pub trait ResetType:
StaticType<MaskType = Bool> StaticType<MaskType = Bool>
+ sealed::ResetTypeSealed + sealed::ResetTypeSealed
+ CastToImpl<Bool, ValueOutput = bool> + ops::ExprCastTo<Bool>
+ CastToImpl<Reset, ValueOutput = SimValue<Reset>> + ops::ExprCastTo<Reset>
+ CastToImpl<SyncReset, ValueOutput = SimValue<SyncReset>> + ops::ExprCastTo<SyncReset>
+ CastToImpl<AsyncReset, ValueOutput = SimValue<AsyncReset>> + ops::ExprCastTo<AsyncReset>
+ CastToImpl<Clock, ValueOutput = SimValue<Clock>> + ops::ExprCastTo<Clock>
+ CastToImpl<UInt<1>, ValueOutput = UIntValue<ConstUsize<1>>> + ops::ExprCastTo<UInt<1>>
+ CastToImpl<SInt<1>, ValueOutput = SIntValue<ConstUsize<1>>> + ops::ExprCastTo<SInt<1>>
+ CastToImpl<UInt, ValueOutput = UIntValue> + ops::ExprCastTo<UInt>
+ CastToImpl<SInt, ValueOutput = SIntValue> + ops::ExprCastTo<SInt>
{ {
fn dispatch<D: ResetTypeDispatch>(input: D::Input<Self>, dispatch: D) -> D::Output<Self>; fn dispatch<D: ResetTypeDispatch>(input: D::Input<Self>, dispatch: D) -> D::Output<Self>;
} }
@ -103,15 +98,6 @@ macro_rules! reset_type {
} }
} }
impl SimValueDebug for $name {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl $name { impl $name {
pub fn type_properties(self) -> TypeProperties { pub fn type_properties(self) -> TypeProperties {
Self::TYPE_PROPERTIES Self::TYPE_PROPERTIES
@ -146,34 +132,29 @@ macro_rules! reset_type {
} }
pub trait $Trait { pub trait $Trait {
type Output: ValueType<Type = $name>; fn $trait_fn(&self) -> Expr<$name>;
fn $trait_fn(&self) -> Self::Output;
} }
impl<T: ?Sized + $Trait> $Trait for &'_ T { impl<T: ?Sized + $Trait> $Trait for &'_ T {
type Output = T::Output; fn $trait_fn(&self) -> Expr<$name> {
fn $trait_fn(&self) -> Self::Output {
(**self).$trait_fn() (**self).$trait_fn()
} }
} }
impl<T: ?Sized + $Trait> $Trait for &'_ mut T { impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
type Output = T::Output; fn $trait_fn(&self) -> Expr<$name> {
fn $trait_fn(&self) -> Self::Output {
(**self).$trait_fn() (**self).$trait_fn()
} }
} }
impl<T: ?Sized + $Trait> $Trait for Box<T> { impl<T: ?Sized + $Trait> $Trait for Box<T> {
type Output = T::Output; fn $trait_fn(&self) -> Expr<$name> {
fn $trait_fn(&self) -> Self::Output {
(**self).$trait_fn() (**self).$trait_fn()
} }
} }
$($impl_trait $Trait for Expr<$name> { $($impl_trait $Trait for Expr<$name> {
type Output = Expr<$name>; fn $trait_fn(&self) -> Expr<$name> {
fn $trait_fn(&self) -> Self::Output {
*self *self
} }
})? })?
@ -190,15 +171,13 @@ reset_type!(
); );
impl ToSyncReset for bool { impl ToSyncReset for bool {
type Output = SimValue<SyncReset>; fn to_sync_reset(&self) -> Expr<SyncReset> {
fn to_sync_reset(&self) -> Self::Output { self.to_expr().to_sync_reset()
SimValue::from_value(SyncReset, *self)
} }
} }
impl ToAsyncReset for bool { impl ToAsyncReset for bool {
type Output = SimValue<AsyncReset>; fn to_async_reset(&self) -> Expr<AsyncReset> {
fn to_async_reset(&self) -> Self::Output { self.to_expr().to_async_reset()
SimValue::from_value(AsyncReset, *self)
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,13 +2,12 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::ValueType,
int::{BoolOrIntType, SInt, UInt}, int::{BoolOrIntType, SInt, UInt},
intern::{Intern, Interned, Memoize}, intern::{Intern, Interned, Memoize},
sim::interpreter::parts::{ sim::interpreter::parts::{
StateLayout, StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, StateLayout, StatePartIndex, StatePartKind, StatePartKindBigSlots, StatePartKindMemories,
StatePartKindMemories, StatePartKindSimOnlySlots, StatePartKindSmallSlots, StatePartLen, StatePartKindSimOnlySlots, StatePartKindSmallSlots, StatePartLen, TypeIndexRange,
TypeIndexRange, TypeLayout, get_state_part_kinds, TypeLayout, get_state_part_kinds,
}, },
source_location::SourceLocation, source_location::SourceLocation,
util::{HashMap, HashSet}, util::{HashMap, HashSet},
@ -17,11 +16,12 @@ use bitvec::slice::BitSlice;
use num_bigint::BigInt; use num_bigint::BigInt;
use num_traits::{One, Signed, ToPrimitive, Zero}; use num_traits::{One, Signed, ToPrimitive, Zero};
use std::{ use std::{
borrow::BorrowMut,
convert::Infallible, convert::Infallible,
fmt::{self, Write}, fmt::{self, Write},
hash::Hash, hash::Hash,
marker::PhantomData, marker::PhantomData,
ops::{ControlFlow, Deref, Index, IndexMut}, ops::{ControlFlow, Deref, DerefMut, Index, IndexMut},
}; };
use vec_map::VecMap; use vec_map::VecMap;
@ -196,27 +196,13 @@ impl fmt::Debug for Insn {
} }
} }
pub(crate) struct PrefixLinesWrapper<'a, W> { struct PrefixLinesWrapper<'a, W> {
writer: W, writer: W,
at_beginning_of_line: bool, at_beginning_of_line: bool,
blank_line_prefix: &'a str, blank_line_prefix: &'a str,
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<T: fmt::Write> fmt::Write for PrefixLinesWrapper<'_, T> { impl<T: fmt::Write> fmt::Write for PrefixLinesWrapper<'_, T> {
fn write_str(&mut self, input: &str) -> fmt::Result { fn write_str(&mut self, input: &str) -> fmt::Result {
for part in input.split_inclusive('\n') { for part in input.split_inclusive('\n') {
@ -253,7 +239,12 @@ impl Insn {
if fields.len() == 0 { if fields.len() == 0 {
return Ok(()); return Ok(());
} }
let mut f = PrefixLinesWrapper::new(f, false, " "); let mut f = PrefixLinesWrapper {
writer: f,
at_beginning_of_line: false,
blank_line_prefix: "",
line_prefix: " ",
};
writeln!(f, " {{")?; writeln!(f, " {{")?;
for (field_name, field) in fields { for (field_name, field) in fields {
write!(f, "{field_name}: ")?; write!(f, "{field_name}: ")?;
@ -329,7 +320,7 @@ impl Insn {
} }
writeln!(f, ",")?; writeln!(f, ",")?;
} }
write!(f.into_inner(), "}}") write!(f.writer, "}}")
} }
} }
@ -914,21 +905,6 @@ impl<K: StatePartKind> StatePart<K> {
value: K::borrow_state(&mut self.value), value: K::borrow_state(&mut self.value),
} }
} }
pub(crate) fn state_index_fetch_maybe_modified_flag(
&self,
part_index: StatePartIndex<K>,
) -> bool {
K::state_index_fetch_maybe_modified_flag(&self.value, part_index)
}
pub(crate) fn state_index_range_fetch_maybe_modified_flags(
&self,
part_index_range: StatePartIndexRange<K>,
) -> bool {
K::state_index_range_fetch_maybe_modified_flags(&self.value, part_index_range)
}
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
K::clear_all_maybe_modified_flags(&mut self.value)
}
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
@ -936,38 +912,56 @@ pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> {
pub(crate) value: K::BorrowedState<'a>, pub(crate) value: K::BorrowedState<'a>,
} }
impl<K: StatePartKind> BorrowedStatePart<'_, K> { impl<
'a,
K: StatePartKind<
BorrowedState<'a>: DerefMut<Target: IndexMut<usize, Output = T> + BorrowMut<[T]>>,
>,
T,
> BorrowedStatePart<'a, K>
{
pub(crate) fn get_disjoint_mut<const N: usize>( pub(crate) fn get_disjoint_mut<const N: usize>(
&mut self, &mut self,
indexes: [StatePartIndex<K>; N], indexes: [StatePartIndex<K>; N],
) -> [&mut K::StateElement; N] { ) -> [&mut T; N] {
K::borrowed_state_get_disjoint_mut(&mut self.value, indexes) (*self.value)
.borrow_mut()
.get_disjoint_mut(indexes.map(|v| v.value as usize))
.expect("indexes are disjoint")
} }
} }
impl<K: StatePartKind> Index<StatePartIndex<K>> for StatePart<K> { impl<K: StatePartKind<State: Deref<Target: Index<usize, Output = T>>>, T> Index<StatePartIndex<K>>
type Output = K::StateElement; for StatePart<K>
{
type Output = T;
fn index(&self, index: StatePartIndex<K>) -> &Self::Output { fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
K::state_index(&self.value, index) &self.value[index.value as usize]
} }
} }
impl<K: StatePartKind> IndexMut<StatePartIndex<K>> for StatePart<K> { impl<K: StatePartKind<State: DerefMut<Target: IndexMut<usize, Output = T>>>, T>
IndexMut<StatePartIndex<K>> for StatePart<K>
{
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output { fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
K::state_index_mut(&mut self.value, index) &mut self.value[index.value as usize]
} }
} }
impl<K: StatePartKind> Index<StatePartIndex<K>> for BorrowedStatePart<'_, K> { impl<'a, K: StatePartKind<BorrowedState<'a>: Deref<Target: Index<usize, Output = T>>>, T>
type Output = K::StateElement; Index<StatePartIndex<K>> for BorrowedStatePart<'a, K>
{
type Output = T;
fn index(&self, index: StatePartIndex<K>) -> &Self::Output { fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
K::borrowed_state_index(&self.value, index) &self.value[index.value as usize]
} }
} }
impl<K: StatePartKind> IndexMut<StatePartIndex<K>> for BorrowedStatePart<'_, K> { impl<'a, K: StatePartKind<BorrowedState<'a>: DerefMut<Target: IndexMut<usize, Output = T>>>, T>
IndexMut<StatePartIndex<K>> for BorrowedStatePart<'a, K>
{
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output { fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
K::borrowed_state_index_mut(&mut self.value, index) &mut self.value[index.value as usize]
} }
} }
@ -980,7 +974,6 @@ macro_rules! make_state {
pub(crate) insns: Interned<Insns<InsnsBuildingDone>>, pub(crate) insns: Interned<Insns<InsnsBuildingDone>>,
pub(crate) pc: usize, pub(crate) pc: usize,
pub(crate) memory_write_log: Vec<(StatePartIndex<StatePartKindMemories>, usize)>, pub(crate) memory_write_log: Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
pub(crate) assert_failed_log: Vec<usize>,
$(pub(crate) $state_plural_field: StatePart<$state_kind>,)* $(pub(crate) $state_plural_field: StatePart<$state_kind>,)*
$(pub(crate) $type_plural_field: StatePart<$type_kind>,)* $(pub(crate) $type_plural_field: StatePart<$type_kind>,)*
} }
@ -991,7 +984,6 @@ macro_rules! make_state {
insns: _, insns: _,
pc, pc,
memory_write_log, memory_write_log,
assert_failed_log,
$($state_plural_field,)* $($state_plural_field,)*
$($type_plural_field,)* $($type_plural_field,)*
} = self; } = self;
@ -999,7 +991,6 @@ macro_rules! make_state {
.field("insns", &InsnsOfState(self)) .field("insns", &InsnsOfState(self))
.field("pc", pc) .field("pc", pc)
.field("memory_write_log", memory_write_log) .field("memory_write_log", memory_write_log)
.field("assert_failed_log", assert_failed_log)
$(.field(stringify!($state_plural_field), $state_plural_field))* $(.field(stringify!($state_plural_field), $state_plural_field))*
$(.field(stringify!($type_plural_field), $type_plural_field))* $(.field(stringify!($type_plural_field), $type_plural_field))*
.finish() .finish()
@ -1012,7 +1003,6 @@ macro_rules! make_state {
insns, insns,
pc: 0, pc: 0,
memory_write_log: Vec::with_capacity(32), memory_write_log: Vec::with_capacity(32),
assert_failed_log: Vec::new(),
$($state_plural_field: StatePart::new(&insns.state_layout.$state_plural_field.layout_data),)* $($state_plural_field: StatePart::new(&insns.state_layout.$state_plural_field.layout_data),)*
$($type_plural_field: StatePart::new(&insns.state_layout.ty.$type_plural_field.layout_data),)* $($type_plural_field: StatePart::new(&insns.state_layout.ty.$type_plural_field.layout_data),)*
} }
@ -1024,20 +1014,10 @@ macro_rules! make_state {
pc: self.pc, pc: self.pc,
orig_pc: &mut self.pc, orig_pc: &mut self.pc,
memory_write_log: &mut self.memory_write_log, memory_write_log: &mut self.memory_write_log,
assert_failed_log: &mut self.assert_failed_log,
$($state_plural_field: self.$state_plural_field.borrow(),)* $($state_plural_field: self.$state_plural_field.borrow(),)*
$($type_plural_field: self.$type_plural_field.borrow(),)* $($type_plural_field: self.$type_plural_field.borrow(),)*
} }
} }
pub(crate) fn type_index_range_fetch_maybe_modified_flags(&self, range: TypeIndexRange) -> bool {
$(self.$type_plural_field.state_index_range_fetch_maybe_modified_flags(
range.$type_plural_field,
))||*
}
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
$(self.$state_plural_field.clear_all_maybe_modified_flags();)*
$(self.$type_plural_field.clear_all_maybe_modified_flags();)*
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -1047,7 +1027,6 @@ macro_rules! make_state {
pub(crate) orig_pc: &'a mut usize, pub(crate) orig_pc: &'a mut usize,
pub(crate) pc: usize, pub(crate) pc: usize,
pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex<StatePartKindMemories>, usize)>, pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
pub(crate) assert_failed_log: &'a mut Vec<usize>,
$(pub(crate) $state_plural_field: BorrowedStatePart<'a, $state_kind>,)* $(pub(crate) $state_plural_field: BorrowedStatePart<'a, $state_kind>,)*
$(pub(crate) $type_plural_field: BorrowedStatePart<'a, $type_kind>,)* $(pub(crate) $type_plural_field: BorrowedStatePart<'a, $type_kind>,)*
} }
@ -1305,7 +1284,6 @@ impl State {
insns: _, insns: _,
pc, pc,
memory_write_log: _, memory_write_log: _,
assert_failed_log: _,
memories: _, memories: _,
small_slots: _, small_slots: _,
big_slots: _, big_slots: _,
@ -1345,10 +1323,6 @@ impl BorrowedState<'_> {
self.memory_write_log.push(log_entry); self.memory_write_log.push(log_entry);
} }
} }
#[cold]
fn assert_failed(&mut self, assert_index: usize) {
self.assert_failed_log.push(assert_index);
}
} }
fn bigint_pow2(width: usize) -> Interned<BigInt> { fn bigint_pow2(width: usize) -> Interned<BigInt> {
@ -2116,19 +2090,6 @@ impl_insns! {
state.log_memory_write(memory, addr); state.log_memory_write(memory, addr);
next!(); next!();
} }
Assert {
#[kind = Input]
clk_triggered: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
pred: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
assert_index: usize,
} => {
if state.small_slots[clk_triggered] != 0 && state.small_slots[pred] == 0 {
state.assert_failed(assert_index);
}
next!();
}
Return => { Return => {
break RunResult::Return(()); break RunResult::Return(());
} }

View file

@ -9,7 +9,7 @@ use crate::{
Insn, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, PrefixLinesWrapper, Insn, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, PrefixLinesWrapper,
SmallSInt, SmallUInt, State, SmallSInt, SmallUInt, State,
}, },
value::{DynSimOnly, DynSimOnlyValue}, value::{DynSimOnlyValue, DynSimOnly},
}, },
ty::CanonicalType, ty::CanonicalType,
util::{chain, const_str_cmp}, util::{chain, const_str_cmp},
@ -236,7 +236,6 @@ pub(crate) trait StatePartKind:
type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy;
type State: fmt::Debug + 'static + Clone; type State: fmt::Debug + 'static + Clone;
type BorrowedState<'a>: 'a; type BorrowedState<'a>: 'a;
type StateElement;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State; fn new_state(layout_data: &[Self::LayoutData]) -> Self::State;
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>;
fn part_debug_data<BK: InsnsBuildingKind>( fn part_debug_data<BK: InsnsBuildingKind>(
@ -248,35 +247,6 @@ pub(crate) trait StatePartKind:
index: StatePartIndex<Self>, index: StatePartIndex<Self>,
f: &mut impl fmt::Write, f: &mut impl fmt::Write,
) -> fmt::Result; ) -> fmt::Result;
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement;
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement;
fn state_index_fetch_maybe_modified_flag(
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool;
fn state_index_range_fetch_maybe_modified_flags(
state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool;
fn clear_all_maybe_modified_flags(state: &mut Self::State);
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement;
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement;
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N];
} }
macro_rules! make_state_part_kinds { macro_rules! make_state_part_kinds {
@ -302,7 +272,6 @@ impl StatePartKind for StatePartKindMemories {
type LayoutData = MemoryData<Interned<BitSlice>>; type LayoutData = MemoryData<Interned<BitSlice>>;
type State = Box<[MemoryData<BitBox>]>; type State = Box<[MemoryData<BitBox>]>;
type BorrowedState<'a> = &'a mut [MemoryData<BitBox>]; type BorrowedState<'a> = &'a mut [MemoryData<BitBox>];
type StateElement = MemoryData<BitBox>;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
layout_data layout_data
.iter() .iter()
@ -328,95 +297,19 @@ impl StatePartKind for StatePartKindMemories {
) -> fmt::Result { ) -> fmt::Result {
write!(f, "{:#?}", &state.memories[index]) write!(f, "{:#?}", &state.memories[index])
} }
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state[part_index.as_usize()]
}
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
&mut state[part_index.as_usize()]
}
fn state_index_fetch_maybe_modified_flag(
_state: &Self::State,
_part_index: StatePartIndex<Self>,
) -> bool {
true
}
fn state_index_range_fetch_maybe_modified_flags(
_state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool {
part_index_range.len.value > 0
}
fn clear_all_maybe_modified_flags(_state: &mut Self::State) {}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state[part_index.as_usize()]
}
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
&mut state[part_index.as_usize()]
}
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N] {
state
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
.expect("indexes are disjoint")
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
pub(crate) struct StateAndModified<T, M> {
pub(crate) state: T,
pub(crate) modified: M,
}
impl<T: Deref<Target = [E]>, M: Deref<Target = [bool]>, E: fmt::Debug> fmt::Debug
for StateAndModified<T, M>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(self.state.iter().zip(self.modified.iter().copied()).map(
|(state, modified)| {
fmt::from_fn(move |f| {
state.fmt(f)?;
if modified {
f.write_str(" (modified)")?;
}
Ok(())
})
},
))
.finish()
}
} }
impl StatePartKind for StatePartKindSmallSlots { impl StatePartKind for StatePartKindSmallSlots {
const NAME: &'static str = "SmallSlots"; const NAME: &'static str = "SmallSlots";
type DebugData = SlotDebugData; type DebugData = SlotDebugData;
type LayoutData = (); type LayoutData = ();
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>; type State = Box<[SmallUInt]>;
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>; type BorrowedState<'a> = &'a mut [SmallUInt];
type StateElement = SmallUInt;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
StateAndModified { vec![0; layout_data.len()].into_boxed_slice()
state: vec![0; layout_data.len()].into_boxed_slice(),
modified: vec![false; layout_data.len()].into_boxed_slice(),
}
} }
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
let StateAndModified { state, modified } = state; state
StateAndModified { state, modified }
} }
fn part_debug_data<BK: InsnsBuildingKind>( fn part_debug_data<BK: InsnsBuildingKind>(
state_layout: &StateLayout<BK>, state_layout: &StateLayout<BK>,
@ -437,80 +330,19 @@ impl StatePartKind for StatePartKindSmallSlots {
write!(f, "{value:#x} {}", value as SmallSInt)?; write!(f, "{value:#x} {}", value as SmallSInt)?;
Ok(()) Ok(())
} }
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn state_index_fetch_maybe_modified_flag(
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool {
state.modified[part_index.as_usize()]
}
fn state_index_range_fetch_maybe_modified_flags(
state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool {
state.modified[part_index_range.start.as_usize()..]
[..part_index_range.len.as_index().as_usize()]
.contains(&true)
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N] {
for part_index in part_indexes {
state.modified[part_index.as_usize()] = true;
}
state
.state
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
.expect("indexes are disjoint")
}
} }
impl StatePartKind for StatePartKindBigSlots { impl StatePartKind for StatePartKindBigSlots {
const NAME: &'static str = "BigSlots"; const NAME: &'static str = "BigSlots";
type DebugData = SlotDebugData; type DebugData = SlotDebugData;
type LayoutData = (); type LayoutData = ();
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>; type State = Box<[BigInt]>;
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>; type BorrowedState<'a> = &'a mut [BigInt];
type StateElement = BigInt;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
let state: Box<[_]> = layout_data.iter().map(|_| BigInt::default()).collect(); layout_data.iter().map(|_| BigInt::default()).collect()
StateAndModified {
modified: vec![false; state.len()].into_boxed_slice(),
state,
}
} }
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
let StateAndModified { state, modified } = state; state
StateAndModified { state, modified }
} }
fn part_debug_data<BK: InsnsBuildingKind>( fn part_debug_data<BK: InsnsBuildingKind>(
state_layout: &StateLayout<BK>, state_layout: &StateLayout<BK>,
@ -529,80 +361,19 @@ impl StatePartKind for StatePartKindBigSlots {
) -> fmt::Result { ) -> fmt::Result {
write!(f, "{:#x}", state.big_slots[index]) write!(f, "{:#x}", state.big_slots[index])
} }
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn state_index_fetch_maybe_modified_flag(
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool {
state.modified[part_index.as_usize()]
}
fn state_index_range_fetch_maybe_modified_flags(
state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool {
state.modified[part_index_range.start.as_usize()..]
[..part_index_range.len.as_index().as_usize()]
.contains(&true)
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N] {
for part_index in part_indexes {
state.modified[part_index.as_usize()] = true;
}
state
.state
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
.expect("indexes are disjoint")
}
} }
impl StatePartKind for StatePartKindSimOnlySlots { impl StatePartKind for StatePartKindSimOnlySlots {
const NAME: &'static str = "SimOnlySlots"; const NAME: &'static str = "SimOnlySlots";
type DebugData = SlotDebugData; type DebugData = SlotDebugData;
type LayoutData = DynSimOnly; type LayoutData = DynSimOnly;
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>; type State = Box<[DynSimOnlyValue]>;
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>; type BorrowedState<'a> = &'a mut [DynSimOnlyValue];
type StateElement = DynSimOnlyValue;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
let state: Box<[_]> = layout_data.iter().map(|ty| ty.default_value()).collect(); layout_data.iter().map(|ty| ty.default_value()).collect()
StateAndModified {
modified: vec![false; state.len()].into_boxed_slice(),
state,
}
} }
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
let StateAndModified { state, modified } = state; state
StateAndModified { state, modified }
} }
fn part_debug_data<BK: InsnsBuildingKind>( fn part_debug_data<BK: InsnsBuildingKind>(
state_layout: &StateLayout<BK>, state_layout: &StateLayout<BK>,
@ -621,61 +392,6 @@ impl StatePartKind for StatePartKindSimOnlySlots {
) -> fmt::Result { ) -> fmt::Result {
write!(f, "{:?}", state.sim_only_slots[index]) write!(f, "{:?}", state.sim_only_slots[index])
} }
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn state_index_fetch_maybe_modified_flag(
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool {
state.modified[part_index.as_usize()]
}
fn state_index_range_fetch_maybe_modified_flags(
state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool {
state.modified[part_index_range.start.as_usize()..]
[..part_index_range.len.as_index().as_usize()]
.contains(&true)
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N] {
for part_index in part_indexes {
state.modified[part_index.as_usize()] = true;
}
state
.state
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
.expect("indexes are disjoint")
}
} }
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -719,7 +435,12 @@ impl<K: StatePartKind> StatePartIndex<K> {
if state.is_some() || debug_data.is_some() { if state.is_some() || debug_data.is_some() {
f.write_str(comment_start)?; f.write_str(comment_start)?;
} }
let mut f = PrefixLinesWrapper::new(f, false, comment_line_start); let mut f = PrefixLinesWrapper {
writer: f,
at_beginning_of_line: false,
blank_line_prefix: comment_line_start.trim_end(),
line_prefix: comment_line_start,
};
if let Some(state) = state { if let Some(state) = state {
f.write_str("(")?; f.write_str("(")?;
K::debug_fmt_state_value(state, *self, &mut f)?; K::debug_fmt_state_value(state, *self, &mut f)?;
@ -732,7 +453,7 @@ impl<K: StatePartKind> StatePartIndex<K> {
write!(f, "{debug_data:?}")?; write!(f, "{debug_data:?}")?;
} }
if state.is_some() || debug_data.is_some() { if state.is_some() || debug_data.is_some() {
f.into_inner().write_str(comment_end)?; f.writer.write_str(comment_end)?;
} }
Ok(()) Ok(())
} }

View file

@ -6,32 +6,28 @@ use crate::{
bundle::{Bundle, BundleType}, bundle::{Bundle, BundleType},
clock::Clock, clock::Clock,
enum_::{Enum, EnumType}, enum_::{Enum, EnumType},
expr::{ expr::{CastBitsTo, Expr, ToExpr},
CastBitsTo, Expr, HdlPartialEq, HdlPartialEqImpl, HdlPartialOrd, ToExpr, ValueType,
value_category::{ValueCategorySimValue, ValueCategoryValue},
},
int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue},
reset::{AsyncReset, Reset, SyncReset}, reset::{AsyncReset, Reset, SyncReset},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSlice, CanonicalType, OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSlice,
OpaqueSimValueWriter, SimValueDebug, StaticType, Type, TypeProperties, OpaqueSimValueWriter, StaticType, Type, TypeProperties, impl_match_variant_as_self,
impl_match_variant_as_self,
}, },
util::{ util::{
ConstUsize, ConstUsize, HashMap,
alternating_cell::{AlternatingCell, AlternatingCellMethods}, alternating_cell::{AlternatingCell, AlternatingCellMethods},
serde_by_id::{SerdeById, SerdeByIdProperties, SerdeByIdTable, SerdeByIdTrait},
}, },
}; };
use bitvec::{slice::BitSlice, vec::BitVec}; use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::hash_map::Entry;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _};
use std::{ use std::{
borrow::{Borrow, BorrowMut, Cow}, borrow::Cow,
fmt, fmt::{self, Write},
num::NonZero, hash::{BuildHasher, Hash, Hasher, RandomState},
ops::{Deref, DerefMut, Index, IndexMut}, ops::{Deref, DerefMut},
sync::Arc, sync::{Arc, Mutex},
}; };
pub(crate) mod sim_only_value_unsafe; pub(crate) mod sim_only_value_unsafe;
@ -140,7 +136,7 @@ impl<T: Type<SimValue: Serialize> + Serialize> Serialize for SimValue<T> {
S: Serializer, S: Serializer,
{ {
SerdeSimValue { SerdeSimValue {
ty: self.ty(), ty: SimValue::ty(self),
value: std::borrow::Cow::Borrowed(&*self), value: std::borrow::Cow::Borrowed(&*self),
} }
.serialize(serializer) .serialize(serializer)
@ -161,22 +157,7 @@ pub struct SimValue<T: Type> {
inner: AlternatingCell<SimValueInner<T>>, inner: AlternatingCell<SimValueInner<T>>,
} }
impl<T: Type> ValueType for SimValue<T> { impl<T: Type + Clone> Clone for SimValue<T> {
type Type = T;
type ValueCategory = ValueCategorySimValue;
fn ty(&self) -> Self::Type {
self.inner.share().ty
}
}
impl<T: Type<SimValue: fmt::Display>> fmt::Display for SimValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::SimValue::fmt(self, f)
}
}
impl<T: Type> Clone for SimValue<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
inner: AlternatingCell::new_unique(self.inner.share().clone()), inner: AlternatingCell::new_unique(self.inner.share().clone()),
@ -231,6 +212,9 @@ impl<T: Type> SimValue<T> {
inner: AlternatingCell::new_unique(inner), inner: AlternatingCell::new_unique(inner),
} }
} }
pub fn ty(this: &Self) -> T {
this.inner.share().ty
}
pub fn into_opaque(this: Self) -> OpaqueSimValue { pub fn into_opaque(this: Self) -> OpaqueSimValue {
this.inner.into_inner().into_opaque() this.inner.into_inner().into_opaque()
} }
@ -269,7 +253,7 @@ impl<T: Type> SimValue<T> {
SimValue::from_opaque(ty.canonical(), opaque) SimValue::from_opaque(ty.canonical(), opaque)
} }
pub fn canonical(this: &Self) -> SimValue<CanonicalType> { pub fn canonical(this: &Self) -> SimValue<CanonicalType> {
SimValue::from_opaque(this.ty().canonical(), Self::opaque(this).clone()) SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone())
} }
#[track_caller] #[track_caller]
pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self
@ -290,7 +274,7 @@ impl<T: Type> SimValue<T> {
where where
T: IntType, T: IntType,
{ {
SimValue::from_opaque(this.ty().as_dyn_int(), Self::opaque(&this).clone()) SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone())
} }
#[track_caller] #[track_caller]
pub fn from_bundle(v: SimValue<Bundle>) -> Self pub fn from_bundle(v: SimValue<Bundle>) -> Self
@ -312,7 +296,7 @@ impl<T: Type> SimValue<T> {
T: BundleType, T: BundleType,
{ {
SimValue::from_opaque( SimValue::from_opaque(
Bundle::from_canonical(this.ty().canonical()), Bundle::from_canonical(Self::ty(this).canonical()),
Self::opaque(&this).clone(), Self::opaque(&this).clone(),
) )
} }
@ -336,7 +320,7 @@ impl<T: Type> SimValue<T> {
T: EnumType, T: EnumType,
{ {
SimValue::from_opaque( SimValue::from_opaque(
Enum::from_canonical(this.ty().canonical()), Enum::from_canonical(Self::ty(this).canonical()),
Self::opaque(&this).clone(), Self::opaque(&this).clone(),
) )
} }
@ -358,11 +342,17 @@ impl<T: Type> DerefMut for SimValue<T> {
impl<T: Type> fmt::Debug for SimValue<T> { impl<T: Type> fmt::Debug for SimValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::SimValue::fmt(&**self, f) let inner = self.inner.share();
f.debug_struct("SimValue")
.field("ty", &inner.ty)
.field("value", &inner.value)
.finish()
} }
} }
impl<T: Type> ToExpr for SimValue<T> { impl<T: Type> ToExpr for SimValue<T> {
type Type = T;
#[track_caller] #[track_caller]
fn to_expr(&self) -> Expr<Self::Type> { fn to_expr(&self) -> Expr<Self::Type> {
let inner = self.inner.share(); let inner = self.inner.share();
@ -370,303 +360,43 @@ impl<T: Type> ToExpr for SimValue<T> {
inner.sim_only_values_len, 0, inner.sim_only_values_len, 0,
"can't convert sim-only values to Expr" "can't convert sim-only values to Expr"
); );
inner.opaque.bits().to_expr().cast_bits_to(inner.ty) inner.opaque.bits().cast_bits_to(inner.ty)
} }
} }
impl<T: Type, Len: Size, I> Index<I> for SimValue<ArrayType<T, Len>> pub trait SimValuePartialEq<T: Type = Self>: Type {
where #[track_caller]
[SimValue<T>]: Index<I>, fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<T>) -> bool;
{
type Output = <[SimValue<T>] as Index<I>>::Output;
fn index(&self, index: I) -> &Self::Output {
(**self).borrow().index(index)
}
} }
impl<T: Type, Len: Size, I> IndexMut<I> for SimValue<ArrayType<T, Len>> impl<T: SimValuePartialEq<U>, U: Type> PartialEq<SimValue<U>> for SimValue<T> {
where
[SimValue<T>]: IndexMut<I>,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
(**self).borrow_mut().index_mut(index)
}
}
impl<T: Type, Len: Size> SimValue<ArrayType<T, Len>> {
pub fn iter(&self) -> std::slice::Iter<'_, SimValue<T>> {
(**self).borrow().iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, SimValue<T>> {
(**self).borrow_mut().iter_mut()
}
}
impl<T: Type, Len: Size> IntoIterator for SimValue<ArrayType<T, Len>> {
type Item = SimValue<T>;
type IntoIter = std::vec::IntoIter<SimValue<T>>;
fn into_iter(self) -> Self::IntoIter {
Vec::into_iter(Self::into_value(self).into())
}
}
impl<'a, T: Type, Len: Size> IntoIterator for &'a SimValue<ArrayType<T, Len>> {
type Item = &'a SimValue<T>;
type IntoIter = std::slice::Iter<'a, SimValue<T>>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T: Type, Len: Size> IntoIterator for &'a mut SimValue<ArrayType<T, Len>> {
type Item = &'a mut SimValue<T>;
type IntoIter = std::slice::IterMut<'a, SimValue<T>>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<T: Type, Len: Size> AsRef<[SimValue<T>]> for SimValue<ArrayType<T, Len>> {
fn as_ref(&self) -> &[SimValue<T>] {
(**self).as_ref()
}
}
impl<T: Type, Len: Size> AsMut<[SimValue<T>]> for SimValue<ArrayType<T, Len>> {
fn as_mut(&mut self) -> &mut [SimValue<T>] {
(**self).as_mut()
}
}
impl<T: Type, U: Type> PartialEq<SimValue<U>> for SimValue<T>
where
Self: for<'a> HdlPartialEq<&'a SimValue<U>, Output = SimValue<Bool>>,
{
#[track_caller] #[track_caller]
fn eq(&self, other: &SimValue<U>) -> bool { fn eq(&self, other: &SimValue<U>) -> bool {
*self.cmp_eq(other) T::sim_value_eq(self, other)
}
#[track_caller]
fn ne(&self, other: &SimValue<U>) -> bool {
*self.cmp_ne(other)
} }
} }
pub trait SimValueEq: Type impl<Width: Size> SimValuePartialEq<Self> for UIntType<Width> {
where fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
for<'a> SimValue<Self>: HdlPartialEq<&'a SimValue<Self>, Output = SimValue<Bool>>, **this == **other
{
}
impl<T: SimValueEq> Eq for SimValue<T> where
for<'a> SimValue<T>: HdlPartialEq<&'a SimValue<T>, Output = SimValue<Bool>>
{
}
impl<T: Type, U: Type> PartialOrd<SimValue<U>> for SimValue<T>
where
Self: for<'a> HdlPartialOrd<&'a SimValue<U>, Output = SimValue<Bool>>,
{
#[track_caller]
fn partial_cmp(&self, other: &SimValue<U>) -> Option<std::cmp::Ordering> {
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<U>) -> bool {
*self.cmp_lt(other)
}
#[track_caller]
fn le(&self, other: &SimValue<U>) -> bool {
*self.cmp_le(other)
}
#[track_caller]
fn gt(&self, other: &SimValue<U>) -> bool {
*self.cmp_gt(other)
}
#[track_caller]
fn ge(&self, other: &SimValue<U>) -> bool {
*self.cmp_ge(other)
} }
} }
pub trait SimValueOrd: SimValueEq impl<Width: Size> SimValuePartialEq<Self> for SIntType<Width> {
where fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
for<'a> SimValue<Self>: HdlPartialOrd<&'a SimValue<Self>, Output = SimValue<Bool>>, **this == **other
{
}
impl<T: SimValueOrd> Ord for SimValue<T>
where
for<'a> SimValue<T>: HdlPartialOrd<&'a SimValue<T>, Output = SimValue<Bool>>,
{
#[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<Width: Size> SimValueEq for UIntType<Width> {} impl SimValuePartialEq<Bool> for Bool {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Bool>) -> bool {
impl<Width: Size> SimValueOrd for UIntType<Width> {} **this == **other
}
impl<Width: Size> SimValueEq for SIntType<Width> {}
impl<Width: Size> SimValueOrd for SIntType<Width> {}
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); pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> {
impl_sim_value_cmp_as_bool!(Clock); type Type: Type;
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, ty::Type};
use std::ops::{Deref, DerefMut};
macro_rules! wrapper {
(
$(pub struct $wrapper:ident<$T:ident>($inner:ty);)*
) => {
$(#[doc(hidden)]
pub struct $wrapper<$T>($inner);
impl<$T> $wrapper<$T> {
#[inline(always)]
pub fn new(value: $T) -> Self {
Self(<$inner>::new(value))
}
}
impl<$T> Deref for $wrapper<$T> {
type Target = $inner;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<$T> DerefMut for $wrapper<$T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
})*
};
}
wrapper! {
pub struct MatchSimValueHelperCheckSimValue<T>(MatchSimValueHelperCheckMutSimValue<T>);
pub struct MatchSimValueHelperCheckMutSimValue<T>(MatchSimValueHelperCheckRefSimValue<T>);
pub struct MatchSimValueHelperCheckRefSimValue<T>(MatchSimValueHelperCheckRefRefSimValue<T>);
pub struct MatchSimValueHelperCheckRefRefSimValue<T>(MatchSimValueHelperCheckRefMutSimValue<T>);
pub struct MatchSimValueHelperCheckRefMutSimValue<T>(MatchSimValueHelperCheckMutRefSimValue<T>);
pub struct MatchSimValueHelperCheckMutRefSimValue<T>(MatchSimValueHelperCheckMutMutSimValue<T>);
pub struct MatchSimValueHelperCheckMutMutSimValue<T>(MatchSimValueHelperIdentity<T>);
}
impl<T: Type> MatchSimValueHelperCheckSimValue<SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> T::SimValue {
SimValue::into_value(self.take())
}
}
impl<'a, T: Type> MatchSimValueHelperCheckMutSimValue<&'a mut SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'a mut T::SimValue {
self.take()
}
}
impl<'a, T: Type> MatchSimValueHelperCheckRefSimValue<&'a SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'a T::SimValue {
self.take()
}
}
impl<'a, 'b, T: Type> MatchSimValueHelperCheckRefRefSimValue<&'a &'b SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'b T::SimValue {
self.take()
}
}
impl<'a, 'b, T: Type> MatchSimValueHelperCheckRefMutSimValue<&'a &'b mut SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'a T::SimValue {
self.take()
}
}
impl<'a, 'b, T: Type> MatchSimValueHelperCheckMutRefSimValue<&'a mut &'b SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'b T::SimValue {
self.take()
}
}
impl<'a, 'b, T: Type> MatchSimValueHelperCheckMutMutSimValue<&'a mut &'b mut SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'a mut T::SimValue {
self.take()
}
}
#[doc(hidden)]
pub struct MatchSimValueHelperIdentity<T>(Option<T>);
impl<T> MatchSimValueHelperIdentity<T> {
fn new(v: T) -> Self {
Self(Some(v))
}
#[inline(always)]
fn take(&mut self) -> T {
self.0.take().expect("known to be Some")
}
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> T {
self.take()
}
}
#[doc(hidden)]
pub type MatchSimValueHelper<T> = MatchSimValueHelperCheckSimValue<T>;
}
pub trait ToSimValue: ToSimValueWithType<<Self as ValueType>::Type> + ValueType {
#[track_caller] #[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type>; fn to_sim_value(&self) -> SimValue<Self::Type>;
#[track_caller] #[track_caller]
@ -708,31 +438,31 @@ pub trait ToSimValueWithType<T: Type> {
macro_rules! forward_to_sim_value_with_type { macro_rules! forward_to_sim_value_with_type {
([$($generics:tt)*] $ty:ty) => { ([$($generics:tt)*] $ty:ty) => {
impl<$($generics)*> ToSimValueWithType<<Self as ValueType>::Type> for $ty { impl<$($generics)*> ToSimValueWithType<<Self as ToSimValue>::Type> for $ty {
fn to_sim_value_with_type(&self, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> { fn to_sim_value_with_type(&self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
let retval = Self::to_sim_value(self); let retval = Self::to_sim_value(self);
assert_eq!(retval.ty(), ty); assert_eq!(SimValue::ty(&retval), ty);
retval retval
} }
#[track_caller] #[track_caller]
fn into_sim_value_with_type(self, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> fn into_sim_value_with_type(self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type>
where where
Self: Sized, Self: Sized,
{ {
let retval = Self::into_sim_value(self); let retval = Self::into_sim_value(self);
assert_eq!(retval.ty(), ty); assert_eq!(SimValue::ty(&retval), ty);
retval retval
} }
#[track_caller] #[track_caller]
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> { fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
let retval = Self::arc_into_sim_value(self); let retval = Self::arc_into_sim_value(self);
assert_eq!(retval.ty(), ty); assert_eq!(SimValue::ty(&retval), ty);
retval retval
} }
#[track_caller] #[track_caller]
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> { fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
let retval = Self::arc_to_sim_value(self); let retval = Self::arc_to_sim_value(self);
assert_eq!(retval.ty(), ty); assert_eq!(SimValue::ty(&retval), ty);
retval retval
} }
} }
@ -740,6 +470,7 @@ macro_rules! forward_to_sim_value_with_type {
} }
impl<T: Type> ToSimValue for SimValue<T> { impl<T: Type> ToSimValue for SimValue<T> {
type Type = T;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
self.clone() self.clone()
} }
@ -795,7 +526,9 @@ impl<T: Type> ToSimValueWithType<T> for BitSlice {
} }
} }
impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This { impl<This: ?Sized + ToSimValue> ToSimValue for &'_ This {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self) This::to_sim_value(self)
} }
@ -808,6 +541,8 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'
} }
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ mut This { impl<This: ?Sized + ToSimValue> ToSimValue for &'_ mut This {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self) This::to_sim_value(self)
} }
@ -820,6 +555,8 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'
} }
impl<This: ?Sized + ToSimValue> ToSimValue for Arc<This> { impl<This: ?Sized + ToSimValue> ToSimValue for Arc<This> {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
This::arc_to_sim_value(self) This::arc_to_sim_value(self)
} }
@ -840,6 +577,7 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for Ar
impl<This: ?Sized + ToSimValue + Send + Sync + 'static> ToSimValue impl<This: ?Sized + ToSimValue + Send + Sync + 'static> ToSimValue
for crate::intern::Interned<This> for crate::intern::Interned<This>
{ {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self) This::to_sim_value(self)
} }
@ -854,6 +592,8 @@ impl<This: ?Sized + ToSimValueWithType<T> + Send + Sync + 'static, T: Type> ToSi
} }
impl<This: ToSimValue> ToSimValue for Box<This> { impl<This: ToSimValue> ToSimValue for Box<This> {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self) This::to_sim_value(self)
} }
@ -896,6 +636,8 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for [
} }
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for [Element] { impl<Element: ToSimValue<Type: StaticType>> ToSimValue for [Element] {
type Type = Array<Element::Type>;
#[track_caller] #[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
@ -931,6 +673,8 @@ impl<Element: ToSimValue<Type: StaticType>, const N: usize> ToSimValue for [Elem
where where
ConstUsize<N>: KnownSize, ConstUsize<N>: KnownSize,
{ {
type Type = Array<Element::Type, N>;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(StaticType::TYPE, self) SimValue::from_array_elements(StaticType::TYPE, self)
} }
@ -984,6 +728,8 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for V
} }
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Vec<Element> { impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Vec<Element> {
type Type = Array<Element::Type>;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
} }
@ -1024,6 +770,8 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for B
} }
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Box<[Element]> { impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Box<[Element]> {
type Type = Array<Element::Type>;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
} }
@ -1053,10 +801,11 @@ impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalTyp
} }
impl<T: Type> ToSimValue for Expr<T> { impl<T: Type> ToSimValue for Expr<T> {
type Type = T;
#[track_caller] #[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_bitslice( SimValue::from_bitslice(
self.ty(), Expr::ty(*self),
&crate::expr::ToLiteralBits::to_literal_bits(self) &crate::expr::ToLiteralBits::to_literal_bits(self)
.expect("must be a literal expression"), .expect("must be a literal expression"),
) )
@ -1076,6 +825,8 @@ macro_rules! impl_to_sim_value_for_bool_like {
} }
impl ToSimValue for bool { impl ToSimValue for bool {
type Type = Bool;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(Bool, *self) SimValue::from_value(Bool, *self)
} }
@ -1097,8 +848,7 @@ impl ToSimValueWithType<CanonicalType> for bool {
| CanonicalType::Enum(_) | CanonicalType::Enum(_)
| CanonicalType::Bundle(_) | CanonicalType::Bundle(_)
| CanonicalType::PhantomConst(_) | CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) | CanonicalType::DynSimOnly(_) => {
| CanonicalType::TraceAsString(_) => {
panic!("can't create SimValue from bool: expected value of type: {ty:?}"); panic!("can't create SimValue from bool: expected value of type: {ty:?}");
} }
CanonicalType::Bool(_) CanonicalType::Bool(_)
@ -1114,8 +864,10 @@ impl ToSimValueWithType<CanonicalType> for bool {
} }
macro_rules! impl_to_sim_value_for_primitive_int { macro_rules! impl_to_sim_value_for_primitive_int {
($prim:ty) => { ($prim:ident) => {
impl ToSimValue for $prim { impl ToSimValue for $prim {
type Type = <$prim as ToExpr>::Type;
#[track_caller] #[track_caller]
fn to_sim_value( fn to_sim_value(
&self, &self,
@ -1126,15 +878,15 @@ macro_rules! impl_to_sim_value_for_primitive_int {
forward_to_sim_value_with_type!([] $prim); forward_to_sim_value_with_type!([] $prim);
impl ToSimValueWithType<<<$prim as ValueType>::Type as IntType>::Dyn> for $prim { impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim {
#[track_caller] #[track_caller]
fn to_sim_value_with_type( fn to_sim_value_with_type(
&self, &self,
ty: <<$prim as ValueType>::Type as IntType>::Dyn, ty: <<$prim as ToExpr>::Type as IntType>::Dyn,
) -> SimValue<<<$prim as ValueType>::Type as IntType>::Dyn> { ) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> {
SimValue::from_value( SimValue::from_value(
ty, ty,
<<$prim as ValueType>::Type as Type>::SimValue::from(*self).as_dyn_int(), <<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(),
) )
} }
} }
@ -1142,7 +894,7 @@ macro_rules! impl_to_sim_value_for_primitive_int {
impl ToSimValueWithType<CanonicalType> for $prim { impl ToSimValueWithType<CanonicalType> for $prim {
#[track_caller] #[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
let ty: <<$prim as ValueType>::Type as IntType>::Dyn = Type::from_canonical(ty); let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty);
SimValue::into_canonical(self.to_sim_value_with_type(ty)) SimValue::into_canonical(self.to_sim_value_with_type(ty))
} }
} }
@ -1161,22 +913,12 @@ impl_to_sim_value_for_primitive_int!(i32);
impl_to_sim_value_for_primitive_int!(i64); impl_to_sim_value_for_primitive_int!(i64);
impl_to_sim_value_for_primitive_int!(i128); impl_to_sim_value_for_primitive_int!(i128);
impl_to_sim_value_for_primitive_int!(isize); impl_to_sim_value_for_primitive_int!(isize);
impl_to_sim_value_for_primitive_int!(NonZero<u8>);
impl_to_sim_value_for_primitive_int!(NonZero<u16>);
impl_to_sim_value_for_primitive_int!(NonZero<u32>);
impl_to_sim_value_for_primitive_int!(NonZero<u64>);
impl_to_sim_value_for_primitive_int!(NonZero<u128>);
impl_to_sim_value_for_primitive_int!(NonZero<usize>);
impl_to_sim_value_for_primitive_int!(NonZero<i8>);
impl_to_sim_value_for_primitive_int!(NonZero<i16>);
impl_to_sim_value_for_primitive_int!(NonZero<i32>);
impl_to_sim_value_for_primitive_int!(NonZero<i64>);
impl_to_sim_value_for_primitive_int!(NonZero<i128>);
impl_to_sim_value_for_primitive_int!(NonZero<isize>);
macro_rules! impl_to_sim_value_for_int_value { macro_rules! impl_to_sim_value_for_int_value {
($IntValue:ident, $Int:ident, $IntType:ident) => { ($IntValue:ident, $Int:ident, $IntType:ident) => {
impl<Width: Size> ToSimValue for $IntValue<Width> { impl<Width: Size> ToSimValue for $IntValue<Width> {
type Type = $IntType<Width>;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(self.ty(), self.clone()) SimValue::from_value(self.ty(), self.clone())
} }
@ -1227,17 +969,80 @@ macro_rules! impl_to_sim_value_for_int_value {
impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType);
impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType);
impl SerdeByIdTrait for DynSimOnly { #[derive(Default)]
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Self> { struct DynSimOnlySerdeTableRest {
self.serde_by_id_properties_inner() from_serde: HashMap<DynSimOnlySerdeId, DynSimOnly>,
} serde_id_random_state: RandomState,
buffer: String,
}
fn static_table() -> &'static SerdeByIdTable<Self> { impl DynSimOnlySerdeTableRest {
static TABLE: SerdeByIdTable<DynSimOnly> = SerdeByIdTable::new(); #[cold]
&TABLE fn add_new(&mut self, ty: DynSimOnly) -> DynSimOnlySerdeId {
let mut try_number = 0u64;
let mut hasher = self.serde_id_random_state.build_hasher();
// extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits
write!(self.buffer, "{:?}", ty.type_id()).expect("shouldn't ever fail");
self.buffer.hash(&mut hasher);
loop {
let mut hasher = hasher.clone();
try_number.hash(&mut hasher);
try_number += 1;
let retval = DynSimOnlySerdeId(std::array::from_fn(|i| {
let mut hasher = hasher.clone();
i.hash(&mut hasher);
hasher.finish() as u32
}));
match self.from_serde.entry(retval) {
Entry::Occupied(_) => continue,
Entry::Vacant(e) => {
e.insert(ty);
return retval;
} }
}
}
}
}
const NAME: &'static str = "DynSimOnly"; #[derive(Default)]
struct DynSimOnlySerdeTable {
to_serde: HashMap<DynSimOnly, DynSimOnlySerdeId>,
rest: DynSimOnlySerdeTableRest,
}
static DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE: Mutex<Option<DynSimOnlySerdeTable>> = Mutex::new(None);
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[serde(transparent)]
struct DynSimOnlySerdeId([u32; 4]);
impl From<DynSimOnly> for DynSimOnlySerdeId {
fn from(ty: DynSimOnly) -> Self {
let mut locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE
.lock()
.expect("shouldn't be poison");
let DynSimOnlySerdeTable { to_serde, rest } = locked.get_or_insert_default();
match to_serde.entry(ty) {
Entry::Occupied(occupied_entry) => *occupied_entry.get(),
Entry::Vacant(vacant_entry) => *vacant_entry.insert(rest.add_new(ty)),
}
}
}
impl DynSimOnlySerdeId {
fn ty(self) -> Option<DynSimOnly> {
let locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE
.lock()
.expect("shouldn't be poison");
Some(*locked.as_ref()?.rest.from_serde.get(&self)?)
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
struct DynSimOnlySerde<'a> {
random_id: DynSimOnlySerdeId,
#[serde(borrow)]
type_name: Cow<'a, str>,
} }
impl Serialize for DynSimOnly { impl Serialize for DynSimOnly {
@ -1245,7 +1050,11 @@ impl Serialize for DynSimOnly {
where where
S: Serializer, S: Serializer,
{ {
SerdeById { inner: *self }.serialize(serializer) DynSimOnlySerde {
random_id: (*self).into(),
type_name: Cow::Borrowed(self.type_name()),
}
.serialize(serializer)
} }
} }
@ -1254,7 +1063,16 @@ impl<'de> Deserialize<'de> for DynSimOnly {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
Ok(SerdeById::deserialize(deserializer)?.inner) let deserialized = DynSimOnlySerde::deserialize(deserializer)?;
let retval = deserialized
.random_id
.ty()
.filter(|ty| ty.type_name() == deserialized.type_name);
retval.ok_or_else(|| {
D::Error::custom(
"doesn't match any DynSimOnly that was serialized this time this program was run",
)
})
} }
} }
@ -1325,15 +1143,6 @@ impl Type for DynSimOnly {
} }
} }
impl SimValueDebug for DynSimOnly {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: SimOnlyValueTrait> Type for SimOnly<T> { impl<T: SimOnlyValueTrait> Type for SimOnly<T> {
type BaseType = DynSimOnly; type BaseType = DynSimOnly;
type MaskType = Bool; type MaskType = Bool;
@ -1399,15 +1208,6 @@ impl<T: SimOnlyValueTrait> Type for SimOnly<T> {
} }
} }
impl<T: SimOnlyValueTrait> SimValueDebug for SimOnly<T> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: SimOnlyValueTrait> StaticType for SimOnly<T> { impl<T: SimOnlyValueTrait> StaticType for SimOnly<T> {
const TYPE: Self = Self::new(); const TYPE: Self = Self::new();
@ -1479,6 +1279,8 @@ impl<T: SimOnlyValueTrait> ToSimValueWithType<SimOnly<T>> for SimOnlyValue<T> {
} }
impl ToSimValue for DynSimOnlyValue { impl ToSimValue for DynSimOnlyValue {
type Type = DynSimOnly;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(self.ty(), self.clone()) SimValue::from_value(self.ty(), self.clone())
} }
@ -1488,62 +1290,15 @@ impl ToSimValue for DynSimOnlyValue {
} }
} }
impl<T: SimOnlyValueTrait> ValueType for SimOnlyValue<T> {
type Type = SimOnly<T>;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
SimOnly::new()
}
}
impl<T: SimOnlyValueTrait> ToSimValue for SimOnlyValue<T> { impl<T: SimOnlyValueTrait> ToSimValue for SimOnlyValue<T> {
type Type = SimOnly<T>;
fn to_sim_value(&self) -> SimValue<Self::Type> { fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(self.ty(), self.clone()) SimValue::from_value(Default::default(), self.clone())
} }
fn into_sim_value(self) -> SimValue<Self::Type> { fn into_sim_value(self) -> SimValue<Self::Type> {
SimValue::from_value(self.ty(), self) SimValue::from_value(Default::default(), self)
}
}
impl HdlPartialEqImpl<Self> for DynSimOnly {
const TRY_STRUCTURAL_EQ: bool = false;
#[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<Self>, _rhs: Expr<Self>) -> Expr<Bool> {
panic!("can't compare Expr<DynSimOnly>");
}
}
impl<L: SimOnlyValueTrait + PartialEq<R>, R: SimOnlyValueTrait> HdlPartialEqImpl<SimOnly<R>>
for SimOnly<L>
{
const TRY_STRUCTURAL_EQ: bool = false;
#[track_caller]
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: SimOnly<R>,
rhs_value: Cow<'_, <SimOnly<R> as Type>::SimValue>,
) -> bool {
**lhs_value == **rhs_value
}
#[track_caller]
fn cmp_expr_eq(_lhs: Expr<Self>, _rhs: Expr<SimOnly<R>>) -> Expr<Bool> {
panic!("can't compare Expr<SimOnly<_>>");
} }
} }

View file

@ -3,10 +3,6 @@
//! `unsafe` parts of [`DynSimOnlyValue`] //! `unsafe` parts of [`DynSimOnlyValue`]
use crate::{
expr::{ValueType, value_category::ValueCategoryValue},
util::serde_by_id::SerdeByIdProperties,
};
use serde::{Serialize, de::DeserializeOwned}; use serde::{Serialize, de::DeserializeOwned};
use std::{ use std::{
any::{self, TypeId}, any::{self, TypeId},
@ -36,7 +32,6 @@ unsafe trait DynSimOnlyTrait: 'static + Send + Sync {
&self, &self,
json_str: &str, json_str: &str,
) -> serde_json::Result<Rc<dyn DynSimOnlyValueTrait>>; ) -> serde_json::Result<Rc<dyn DynSimOnlyValueTrait>>;
fn serde_by_id_properties_inner(&self) -> SerdeByIdProperties<DynSimOnly>;
} }
/// Safety: `type_id_dyn` is implemented correctly /// Safety: `type_id_dyn` is implemented correctly
@ -59,9 +54,6 @@ unsafe impl<T: SimOnlyValueTrait> DynSimOnlyTrait for SimOnly<T> {
) -> serde_json::Result<Rc<dyn DynSimOnlyValueTrait>> { ) -> serde_json::Result<Rc<dyn DynSimOnlyValueTrait>> {
Ok(Rc::<T>::new(serde_json::from_str(json_str)?)) Ok(Rc::<T>::new(serde_json::from_str(json_str)?))
} }
fn serde_by_id_properties_inner(&self) -> SerdeByIdProperties<DynSimOnly> {
SerdeByIdProperties::of::<T>()
}
} }
/// Safety: /// Safety:
@ -158,9 +150,6 @@ impl DynSimOnly {
pub fn default_value(self) -> DynSimOnlyValue { pub fn default_value(self) -> DynSimOnlyValue {
DynSimOnlyValue(self.ty.default_value()) DynSimOnlyValue(self.ty.default_value())
} }
pub(super) fn serde_by_id_properties_inner(self) -> SerdeByIdProperties<Self> {
self.ty.serde_by_id_properties_inner()
}
} }
impl PartialEq for DynSimOnly { impl PartialEq for DynSimOnly {
@ -217,25 +206,9 @@ impl<T: SimOnlyValueTrait> Default for SimOnly<T> {
} }
/// a value that can only be used in a Fayalite simulation, it can't be converted to FIRRTL /// a value that can only be used in a Fayalite simulation, it can't be converted to FIRRTL
#[derive(Clone, Eq, Hash, Default, Ord)] #[derive(Clone, Eq, PartialEq, Hash, Default, PartialOrd, Ord)]
pub struct SimOnlyValue<T: SimOnlyValueTrait>(Rc<T>); pub struct SimOnlyValue<T: SimOnlyValueTrait>(Rc<T>);
impl<T: SimOnlyValueTrait + PartialEq<U>, U: SimOnlyValueTrait> PartialEq<SimOnlyValue<U>>
for SimOnlyValue<T>
{
fn eq(&self, other: &SimOnlyValue<U>) -> bool {
<T as PartialEq<U>>::eq(self, other)
}
}
impl<T: SimOnlyValueTrait + PartialOrd<U>, U: SimOnlyValueTrait> PartialOrd<SimOnlyValue<U>>
for SimOnlyValue<T>
{
fn partial_cmp(&self, other: &SimOnlyValue<U>) -> Option<std::cmp::Ordering> {
<T as PartialOrd<U>>::partial_cmp(self, other)
}
}
impl<T: SimOnlyValueTrait> SimOnlyValue<T> { impl<T: SimOnlyValueTrait> SimOnlyValue<T> {
pub fn with_dyn_ref<F: FnOnce(&DynSimOnlyValue) -> R, R>(&self, f: F) -> R { pub fn with_dyn_ref<F: FnOnce(&DynSimOnlyValue) -> R, R>(&self, f: F) -> R {
// Safety: creating a copied `Rc<T>` is safe as long as the copy isn't dropped and isn't changed // Safety: creating a copied `Rc<T>` is safe as long as the copy isn't dropped and isn't changed
@ -306,16 +279,10 @@ impl<T: SimOnlyValueTrait> From<SimOnlyValue<T>> for DynSimOnlyValue {
} }
} }
impl ValueType for DynSimOnlyValue { impl DynSimOnlyValue {
type Type = DynSimOnly; pub fn ty(&self) -> DynSimOnly {
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
self.0.ty() self.0.ty()
} }
}
impl DynSimOnlyValue {
pub fn type_id(&self) -> TypeId { pub fn type_id(&self) -> TypeId {
self.0.type_id_dyn() self.0.type_id_dyn()
} }

View file

@ -6,47 +6,27 @@ use crate::{
expr::Flow, expr::Flow,
int::UInt, int::UInt,
intern::{Intern, Interned}, intern::{Intern, Interned},
prelude::PhantomConst,
sim::{ sim::{
TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl,
TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceFormalInput, TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceInstance,
TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceModule,
TraceModule, TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt, TraceScalar, TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSimOnly,
TraceScalarId, TraceScope, TraceSimOnly, TraceSyncReset, TraceTraceAsString, TraceUInt, TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls,
TraceWire, TraceWriter, TraceWriterDecls,
time::{SimDuration, SimInstant}, time::{SimDuration, SimInstant},
value::DynSimOnlyValue, value::DynSimOnlyValue,
}, },
ty::{OpaqueSimValueSlice, TraceAsString},
util::HashMap, util::HashMap,
}; };
use bitvec::{order::Lsb0, slice::BitSlice}; use bitvec::{order::Lsb0, slice::BitSlice};
use hashbrown::hash_map::Entry; use hashbrown::hash_map::Entry;
use sha2::{Digest, Sha256};
use std::{ use std::{
collections::BTreeMap,
fmt::{self, Write as _}, fmt::{self, Write as _},
io, mem, io, mem,
num::NonZeroU64,
}; };
#[derive(Default, Clone)] #[derive(Default)]
struct PathHash(Sha256);
impl PathHash {
fn joined(mut self, segment: impl AsRef<[u8]>) -> Self {
let segment = segment.as_ref();
self.0.update(u32::to_le_bytes(
segment.len().try_into().expect("path segment is too big"),
));
self.0.update(segment);
self
}
}
struct Scope { struct Scope {
last_inserted: HashMap<Interned<str>, usize>, last_inserted: HashMap<Interned<str>, usize>,
path_hash: PathHash,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -79,13 +59,6 @@ impl fmt::Display for VerilogIdentifier {
} }
impl Scope { impl Scope {
fn new(path_hash: PathHash) -> Self {
Self {
last_inserted: Default::default(),
path_hash,
}
}
fn new_identifier(&mut self, unescaped_name: Interned<str>) -> VerilogIdentifier { fn new_identifier(&mut self, unescaped_name: Interned<str>) -> VerilogIdentifier {
let next_disambiguator = match self.last_inserted.entry(unescaped_name) { let next_disambiguator = match self.last_inserted.entry(unescaped_name) {
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
@ -188,26 +161,6 @@ impl<W: io::Write> fmt::Debug for VcdWriterDecls<W> {
} }
} }
/// pass in scope to ensure it's not available in child scope
fn try_write_vcd_scope<W: io::Write, R>(
writer: &mut W,
scope_type: &str,
scope_name: Interned<str>,
scope: Option<&mut Scope>,
f: impl FnOnce(&mut W, Option<&mut Scope>) -> io::Result<R>,
) -> io::Result<R> {
let Some(scope) = scope else {
return f(writer, None);
};
write_vcd_scope(
writer,
scope_type,
scope_name,
scope,
move |writer, scope| f(writer, Some(scope)),
)
}
/// pass in scope to ensure it's not available in child scope /// pass in scope to ensure it's not available in child scope
fn write_vcd_scope<W: io::Write, R>( fn write_vcd_scope<W: io::Write, R>(
writer: &mut W, writer: &mut W,
@ -216,10 +169,12 @@ fn write_vcd_scope<W: io::Write, R>(
scope: &mut Scope, scope: &mut Scope,
f: impl FnOnce(&mut W, &mut Scope) -> io::Result<R>, f: impl FnOnce(&mut W, &mut Scope) -> io::Result<R>,
) -> io::Result<R> { ) -> io::Result<R> {
let path_hash = scope.path_hash.clone().joined(scope_name); writeln!(
let scope_name = scope.new_identifier(scope_name); writer,
writeln!(writer, "$scope {scope_type} {scope_name} $end")?; "$scope {scope_type} {} $end",
let retval = f(writer, &mut Scope::new(path_hash))?; scope.new_identifier(scope_name),
)?;
let retval = f(writer, &mut Scope::default())?;
writeln!(writer, "$upscope $end")?; writeln!(writer, "$upscope $end")?;
Ok(retval) Ok(retval)
} }
@ -259,7 +214,6 @@ trait_arg! {
struct ArgModule<'a> { struct ArgModule<'a> {
properties: &'a mut VcdWriterProperties, properties: &'a mut VcdWriterProperties,
scope: &'a mut Scope, scope: &'a mut Scope,
instance_name: Option<Interned<str>>,
} }
impl<'a> ArgModule<'a> { impl<'a> ArgModule<'a> {
@ -267,7 +221,6 @@ impl<'a> ArgModule<'a> {
ArgModule { ArgModule {
properties: self.properties, properties: self.properties,
scope: self.scope, scope: self.scope,
instance_name: self.instance_name,
} }
} }
} }
@ -291,7 +244,7 @@ struct ArgInType<'a> {
sink_var_type: &'static str, sink_var_type: &'static str,
duplex_var_type: &'static str, duplex_var_type: &'static str,
properties: &'a mut VcdWriterProperties, properties: &'a mut VcdWriterProperties,
scope: Option<&'a mut Scope>, scope: &'a mut Scope,
} }
impl<'a> ArgInType<'a> { impl<'a> ArgInType<'a> {
@ -301,7 +254,7 @@ impl<'a> ArgInType<'a> {
sink_var_type: self.sink_var_type, sink_var_type: self.sink_var_type,
duplex_var_type: self.duplex_var_type, duplex_var_type: self.duplex_var_type,
properties: self.properties, properties: self.properties,
scope: self.scope.as_deref_mut(), scope: self.scope,
} }
} }
} }
@ -330,85 +283,24 @@ impl WriteTrace for TraceScalar {
Self::Clock(v) => v.write_trace(writer, arg), Self::Clock(v) => v.write_trace(writer, arg),
Self::SyncReset(v) => v.write_trace(writer, arg), Self::SyncReset(v) => v.write_trace(writer, arg),
Self::AsyncReset(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), Self::SimOnly(v) => v.write_trace(writer, arg),
Self::TraceAsString(v) => v.write_trace(writer, arg),
} }
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] fn write_vcd_id<W: io::Write>(writer: &mut W, mut id: usize) -> io::Result<()> {
#[repr(transparent)] let min_char = b'!';
struct VcdId(NonZeroU64); let max_char = b'~';
let base = (max_char - min_char + 1) as usize;
impl VcdId {
const CHAR_RANGE: std::ops::RangeInclusive<u8> = b'!'..=b'~';
const BASE: u8 = *Self::CHAR_RANGE.end() - *Self::CHAR_RANGE.start() + 1;
const LOW_HALF_CHARS: u32 = 5;
const LOW_HALF_MODULUS: u64 = (Self::BASE as u64).pow(Self::LOW_HALF_CHARS);
const fn from_str(s: &str) -> Option<Self> {
if s.is_empty() {
return None;
}
let mut retval = 0u64;
let mut bytes = s.as_bytes();
while let [ref rest @ .., digit] = *bytes {
bytes = rest;
let Some(digit) = digit.checked_sub(*Self::CHAR_RANGE.start()) else {
return None;
};
if digit >= Self::BASE {
return None;
}
let Some(v) = retval.checked_mul(Self::BASE as _) else {
return None;
};
let Some(v) = v.checked_add(digit as _) else {
return None;
};
retval = v;
}
let Some(retval) = NonZeroU64::new(retval) else {
return None;
};
Some(Self(retval))
}
#[must_use]
const fn write(self, out: &mut [u8]) -> usize {
let mut id = self.0.get();
let mut len = 0;
loop { loop {
let digit = (id % Self::BASE as u64) as u8 + *Self::CHAR_RANGE.start(); let digit = (id % base) as u8 + min_char;
id /= Self::BASE as u64; id /= base;
if len < out.len() { writer.write_all(&[digit])?;
out[len] = digit;
}
len += 1;
if id == 0 { if id == 0 {
break; break;
} }
} }
len Ok(())
}
const MAX_ID_LEN: usize = Self(NonZeroU64::MAX).write(&mut []);
}
/// check that VcdId properly round-trips
const _: () = {
let s = "RoundTrip";
let Some(id) = VcdId::from_str(s) else {
unreachable!();
};
let mut buf = [0u8; VcdId::MAX_ID_LEN];
let len = id.write(&mut buf);
assert!(crate::util::const_bytes_cmp(buf.split_at(len).0, s.as_bytes()).is_eq());
};
fn write_vcd_id<W: io::Write>(writer: &mut W, id: VcdId) -> io::Result<()> {
let mut buf = [0u8; VcdId::MAX_ID_LEN];
let len = id.write(&mut buf);
writer.write_all(&buf[..len])
} }
struct Escaped<T: fmt::Display>(T); struct Escaped<T: fmt::Display>(T);
@ -451,13 +343,12 @@ impl<T: fmt::Display> fmt::Display for Escaped<T> {
fn write_vcd_var<W: io::Write>( fn write_vcd_var<W: io::Write>(
properties: &mut VcdWriterProperties, properties: &mut VcdWriterProperties,
scope: Option<&mut Scope>,
memory_element_part_body: MemoryElementPartBody, memory_element_part_body: MemoryElementPartBody,
writer: &mut W, writer: &mut W,
var_type: &str, var_type: &str,
size: usize, size: usize,
location: TraceLocation, location: TraceLocation,
name: Interned<str>, name: VerilogIdentifier,
) -> io::Result<()> { ) -> io::Result<()> {
let id = match location { let id = match location {
TraceLocation::Scalar(id) => id.as_usize(), TraceLocation::Scalar(id) => id.as_usize(),
@ -490,21 +381,9 @@ fn write_vcd_var<W: io::Write>(
first_id + *element_index first_id + *element_index
} }
}; };
if let Some(scope) = scope {
let path_hash = scope.path_hash.clone().joined(name);
let name = scope.new_identifier(name);
let id = properties
.scalar_id_to_vcd_id_map
.builder_get_or_insert(id, &path_hash);
write!(writer, "$var {var_type} {size} ")?; write!(writer, "$var {var_type} {size} ")?;
write_vcd_id(writer, id)?; write_vcd_id(writer, id)?;
writeln!(writer, " {name} $end") writeln!(writer, " {name} $end")
} else {
properties
.scalar_id_to_vcd_id_map
.builder_unused_scalar_id(id);
Ok(())
}
} }
impl WriteTrace for TraceUInt { impl WriteTrace for TraceUInt {
@ -532,13 +411,12 @@ impl WriteTrace for TraceUInt {
} }
write_vcd_var( write_vcd_var(
properties, properties,
scope,
MemoryElementPartBody::Scalar, MemoryElementPartBody::Scalar,
writer, writer,
var_type, var_type,
ty.width(), ty.width(),
location, location,
name, scope.new_identifier(name),
) )
} }
} }
@ -613,13 +491,12 @@ impl WriteTrace for TraceEnumDiscriminant {
} = self; } = self;
write_vcd_var( write_vcd_var(
properties, properties,
scope,
MemoryElementPartBody::EnumDiscriminant { ty }, MemoryElementPartBody::EnumDiscriminant { ty },
writer, writer,
"string", "string",
1, 1,
location, location,
name, scope.new_identifier(name),
) )
} }
} }
@ -672,34 +549,6 @@ impl WriteTrace for TraceAsyncReset {
} }
} }
impl WriteTrace for TracePhantomConst {
fn write_trace<W: io::Write, A: Arg>(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,
scope,
MemoryElementPartBody::Scalar,
writer,
"string",
1,
location,
name,
)
}
}
impl WriteTrace for TraceSimOnly { impl WriteTrace for TraceSimOnly {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgInType { let ArgInType {
@ -717,41 +566,12 @@ impl WriteTrace for TraceSimOnly {
} = self; } = self;
write_vcd_var( write_vcd_var(
properties, properties,
scope,
MemoryElementPartBody::Scalar, MemoryElementPartBody::Scalar,
writer, writer,
"string", "string",
1, 1,
location, location,
name, scope.new_identifier(name),
)
}
}
impl WriteTrace for TraceTraceAsString {
fn write_trace<W: io::Write, A: Arg>(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,
scope,
MemoryElementPartBody::TraceAsString { ty },
writer,
"string",
1,
location,
name,
) )
} }
} }
@ -766,7 +586,6 @@ impl WriteTrace for TraceScope {
Self::Wire(v) => v.write_trace(writer, arg), Self::Wire(v) => v.write_trace(writer, arg),
Self::Reg(v) => v.write_trace(writer, arg), Self::Reg(v) => v.write_trace(writer, arg),
Self::ModuleIO(v) => v.write_trace(writer, arg), Self::ModuleIO(v) => v.write_trace(writer, arg),
Self::FormalInput(v) => v.write_trace(writer, arg),
Self::Bundle(v) => v.write_trace(writer, arg), Self::Bundle(v) => v.write_trace(writer, arg),
Self::Array(v) => v.write_trace(writer, arg), Self::Array(v) => v.write_trace(writer, arg),
Self::EnumWithFields(v) => v.write_trace(writer, arg), Self::EnumWithFields(v) => v.write_trace(writer, arg),
@ -776,24 +595,14 @@ impl WriteTrace for TraceScope {
impl WriteTrace for TraceModule { impl WriteTrace for TraceModule {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModule { let ArgModule { properties, scope } = arg.module();
properties,
scope,
instance_name,
} = arg.module();
let Self { name, children } = self; let Self { name, children } = self;
write_vcd_scope( write_vcd_scope(writer, "module", name, scope, |writer, scope| {
writer,
"module",
instance_name.unwrap_or(name),
scope,
|writer, scope| {
for child in children { for child in children {
child.write_trace(writer, ArgModuleBody { properties, scope })?; child.write_trace(writer, ArgModuleBody { properties, scope })?;
} }
Ok(()) Ok(())
}, })
)
} }
} }
@ -801,7 +610,7 @@ impl WriteTrace for TraceInstance {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties, scope } = arg.module_body(); let ArgModuleBody { properties, scope } = arg.module_body();
let Self { let Self {
name, name: _,
instance_io, instance_io,
module, module,
ty: _, ty: _,
@ -813,17 +622,10 @@ impl WriteTrace for TraceInstance {
sink_var_type: "wire", sink_var_type: "wire",
duplex_var_type: "wire", duplex_var_type: "wire",
properties, properties,
scope: None, scope,
}, },
)?; )?;
module.write_trace( module.write_trace(writer, ArgModule { properties, scope })
writer,
ArgModule {
properties,
scope,
instance_name: Some(name),
},
)
} }
} }
@ -862,7 +664,7 @@ impl WriteTrace for TraceMem {
sink_var_type: "reg", sink_var_type: "reg",
duplex_var_type: "reg", duplex_var_type: "reg",
properties, properties,
scope: Some(scope), scope,
}, },
) )
}, },
@ -894,7 +696,7 @@ impl WriteTrace for TraceMemPort {
sink_var_type: "wire", sink_var_type: "wire",
duplex_var_type: "wire", duplex_var_type: "wire",
properties, properties,
scope: Some(scope), scope,
}, },
) )
} }
@ -915,7 +717,7 @@ impl WriteTrace for TraceWire {
sink_var_type: "wire", sink_var_type: "wire",
duplex_var_type: "wire", duplex_var_type: "wire",
properties, properties,
scope: Some(scope), scope,
}, },
) )
} }
@ -936,7 +738,7 @@ impl WriteTrace for TraceReg {
sink_var_type: "reg", sink_var_type: "reg",
duplex_var_type: "reg", duplex_var_type: "reg",
properties, properties,
scope: Some(scope), scope,
}, },
) )
} }
@ -958,28 +760,7 @@ impl WriteTrace for TraceModuleIO {
sink_var_type: "wire", sink_var_type: "wire",
duplex_var_type: "wire", duplex_var_type: "wire",
properties, properties,
scope: Some(scope), scope,
},
)
}
}
impl WriteTrace for TraceFormalInput {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties, scope } = arg.module_body();
let Self {
name: _,
child,
formal_input: _,
} = self;
child.write_trace(
writer,
ArgInType {
source_var_type: "wire",
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
scope: Some(scope),
}, },
) )
} }
@ -1000,7 +781,7 @@ impl WriteTrace for TraceBundle {
ty: _, ty: _,
flow: _, flow: _,
} = self; } = self;
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| { write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
for field in fields { for field in fields {
field.write_trace( field.write_trace(
writer, writer,
@ -1009,7 +790,7 @@ impl WriteTrace for TraceBundle {
sink_var_type, sink_var_type,
duplex_var_type, duplex_var_type,
properties, properties,
scope: scope.as_deref_mut(), scope,
}, },
)?; )?;
} }
@ -1033,7 +814,7 @@ impl WriteTrace for TraceArray {
ty: _, ty: _,
flow: _, flow: _,
} = self; } = self;
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| { write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
for element in elements { for element in elements {
element.write_trace( element.write_trace(
writer, writer,
@ -1042,7 +823,7 @@ impl WriteTrace for TraceArray {
sink_var_type, sink_var_type,
duplex_var_type, duplex_var_type,
properties, properties,
scope: scope.as_deref_mut(), scope,
}, },
)?; )?;
} }
@ -1067,7 +848,7 @@ impl WriteTrace for TraceEnumWithFields {
ty: _, ty: _,
flow: _, flow: _,
} = self; } = self;
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| { write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
discriminant.write_trace( discriminant.write_trace(
writer, writer,
ArgInType { ArgInType {
@ -1075,7 +856,7 @@ impl WriteTrace for TraceEnumWithFields {
sink_var_type, sink_var_type,
duplex_var_type, duplex_var_type,
properties, properties,
scope: scope.as_deref_mut(), scope,
}, },
)?; )?;
for field in non_empty_fields { for field in non_empty_fields {
@ -1086,7 +867,7 @@ impl WriteTrace for TraceEnumWithFields {
sink_var_type, sink_var_type,
duplex_var_type, duplex_var_type,
properties, properties,
scope: scope.as_deref_mut(), scope,
}, },
)?; )?;
} }
@ -1112,9 +893,6 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
writeln!(writer, "$timescale {} $end", vcd_timescale(timescale))?; writeln!(writer, "$timescale {} $end", vcd_timescale(timescale))?;
let mut properties = VcdWriterProperties { let mut properties = VcdWriterProperties {
next_scalar_id: trace_scalar_id_count, next_scalar_id: trace_scalar_id_count,
scalar_id_to_vcd_id_map: ScalarIdToVcdIdMapOrBuilder::Builder(
ScalarIdToVcdIdMapBuilder::default(),
),
memory_properties: (0..trace_memory_id_count) memory_properties: (0..trace_memory_id_count)
.map(|_| MemoryProperties { .map(|_| MemoryProperties {
element_parts: Vec::with_capacity(8), element_parts: Vec::with_capacity(8),
@ -1127,17 +905,9 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
&mut writer, &mut writer,
ArgModule { ArgModule {
properties: &mut properties, properties: &mut properties,
scope: &mut Scope::new(PathHash::default()), scope: &mut Scope::default(),
instance_name: None,
}, },
)?; )?;
let ScalarIdToVcdIdMapOrBuilder::Builder(scalar_id_to_vcd_id_map_builder) =
properties.scalar_id_to_vcd_id_map
else {
unreachable!();
};
properties.scalar_id_to_vcd_id_map =
ScalarIdToVcdIdMapOrBuilder::Built(scalar_id_to_vcd_id_map_builder.build());
writeln!(writer, "$enddefinitions $end")?; writeln!(writer, "$enddefinitions $end")?;
writeln!(writer, "$dumpvars")?; writeln!(writer, "$dumpvars")?;
Ok(VcdWriter { Ok(VcdWriter {
@ -1145,7 +915,6 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
finished_init: false, finished_init: false,
timescale, timescale,
properties, properties,
trace_as_string_buf: String::with_capacity(256),
}) })
} }
} }
@ -1153,7 +922,6 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
enum MemoryElementPartBody { enum MemoryElementPartBody {
Scalar, Scalar,
EnumDiscriminant { ty: Enum }, EnumDiscriminant { ty: Enum },
TraceAsString { ty: TraceAsString },
} }
struct MemoryElementPart { struct MemoryElementPart {
@ -1169,100 +937,8 @@ struct MemoryProperties {
element_index: usize, element_index: usize,
} }
struct ScalarIdToVcdIdMap {
scalar_id_to_vcd_id_map: Box<[Option<VcdId>]>,
}
#[derive(Default)]
struct ScalarIdToVcdIdMapBuilder {
scalar_id_to_vcd_id_map: BTreeMap<usize, Option<VcdId>>,
lower_half_to_next_upper_half_map: HashMap<u64, u64>,
}
impl ScalarIdToVcdIdMapBuilder {
fn unused_scalar_id(&mut self, scalar_id: usize) {
self.scalar_id_to_vcd_id_map
.entry(scalar_id)
.or_insert(None);
}
/// `VcdId`s are based off of `path_hash` (and not `scalar_id`) since the hash doesn't change
/// when unrelated variables are added/removed, making the generated VCD more friendly for git diff.
fn get_or_insert(&mut self, scalar_id: usize, path_hash: &PathHash) -> VcdId {
*self
.scalar_id_to_vcd_id_map
.entry(scalar_id)
.or_insert(None)
.get_or_insert_with(|| {
let hash = u128::from_le_bytes(
*path_hash
.0
.clone()
.finalize()
.first_chunk()
.expect("known to be bigger than u128"),
);
let lower_half = (hash % VcdId::LOW_HALF_MODULUS as u128) as u64;
let next_upper_half = self
.lower_half_to_next_upper_half_map
.entry(lower_half)
.or_insert(if lower_half == 0 { 1 } else { 0 });
let upper_half = *next_upper_half;
*next_upper_half += 1;
let Some(id) = upper_half
.checked_mul(VcdId::LOW_HALF_MODULUS)
.and_then(|v| v.checked_add(lower_half))
else {
panic!("too many VcdIds");
};
VcdId(NonZeroU64::new(id).expect("known to not be zero"))
})
}
fn build(self) -> ScalarIdToVcdIdMap {
ScalarIdToVcdIdMap {
scalar_id_to_vcd_id_map: self
.scalar_id_to_vcd_id_map
.into_iter()
.enumerate()
.map(|(index, (scalar_id, vcd_id))| {
if index != scalar_id {
panic!("missing scalar id {index}");
}
vcd_id
})
.collect(),
}
}
}
enum ScalarIdToVcdIdMapOrBuilder {
Builder(ScalarIdToVcdIdMapBuilder),
Built(ScalarIdToVcdIdMap),
}
impl ScalarIdToVcdIdMapOrBuilder {
fn built_scalar_id_to_vcd_id(&self, scalar_id: usize) -> Option<VcdId> {
let Self::Built(v) = self else {
panic!("ScalarIdToVcdIdMap isn't built yet");
};
v.scalar_id_to_vcd_id_map[scalar_id]
}
fn builder_get_or_insert(&mut self, scalar_id: usize, path_hash: &PathHash) -> VcdId {
let Self::Builder(v) = self else {
panic!("ScalarIdToVcdIdMap is already built");
};
v.get_or_insert(scalar_id, path_hash)
}
fn builder_unused_scalar_id(&mut self, scalar_id: usize) {
let Self::Builder(v) = self else {
panic!("ScalarIdToVcdIdMap is already built");
};
v.unused_scalar_id(scalar_id)
}
}
struct VcdWriterProperties { struct VcdWriterProperties {
next_scalar_id: usize, next_scalar_id: usize,
scalar_id_to_vcd_id_map: ScalarIdToVcdIdMapOrBuilder,
memory_properties: Box<[MemoryProperties]>, memory_properties: Box<[MemoryProperties]>,
} }
@ -1271,7 +947,6 @@ pub struct VcdWriter<W: io::Write + 'static> {
finished_init: bool, finished_init: bool,
timescale: SimDuration, timescale: SimDuration,
properties: VcdWriterProperties, properties: VcdWriterProperties,
trace_as_string_buf: String,
} }
impl<W: io::Write + 'static> VcdWriter<W> { impl<W: io::Write + 'static> VcdWriter<W> {
@ -1283,11 +958,8 @@ impl<W: io::Write + 'static> VcdWriter<W> {
fn write_string_value_change( fn write_string_value_change(
writer: &mut impl io::Write, writer: &mut impl io::Write,
value: impl fmt::Display, value: impl fmt::Display,
id: Option<VcdId>, id: usize,
) -> io::Result<()> { ) -> io::Result<()> {
let Some(id) = id else {
return Ok(());
};
write!(writer, "s{} ", Escaped(value))?; write!(writer, "s{} ", Escaped(value))?;
write_vcd_id(writer, id)?; write_vcd_id(writer, id)?;
writer.write_all(b"\n") writer.write_all(b"\n")
@ -1296,11 +968,8 @@ fn write_string_value_change(
fn write_bits_value_change( fn write_bits_value_change(
writer: &mut impl io::Write, writer: &mut impl io::Write,
value: &BitSlice, value: &BitSlice,
id: Option<VcdId>, id: usize,
) -> io::Result<()> { ) -> io::Result<()> {
let Some(id) = id else {
return Ok(());
};
match value.len() { match value.len() {
0 => writer.write_all(b"s0 ")?, 0 => writer.write_all(b"s0 ")?,
1 => writer.write_all(if value[0] { b"1" } else { b"0" })?, 1 => writer.write_all(if value[0] { b"1" } else { b"0" })?,
@ -1329,7 +998,7 @@ fn write_enum_discriminant_value_change(
writer: &mut impl io::Write, writer: &mut impl io::Write,
variant_index: usize, variant_index: usize,
ty: Enum, ty: Enum,
id: Option<VcdId>, id: usize,
) -> io::Result<()> { ) -> io::Result<()> {
write_string_value_change( write_string_value_change(
writer, writer,
@ -1364,9 +1033,7 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
MemoryElementPartBody::Scalar => write_bits_value_change( MemoryElementPartBody::Scalar => write_bits_value_change(
&mut self.writer, &mut self.writer,
&element_data[start..start + len], &element_data[start..start + len],
self.properties first_id + element_index,
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(first_id + element_index),
)?, )?,
MemoryElementPartBody::EnumDiscriminant { ty } => { MemoryElementPartBody::EnumDiscriminant { ty } => {
let mut variant_index = 0; let mut variant_index = 0;
@ -1376,49 +1043,20 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
&mut self.writer, &mut self.writer,
variant_index, variant_index,
*ty, *ty,
self.properties first_id + element_index,
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(first_id + element_index),
)? )?
} }
MemoryElementPartBody::TraceAsString { ty } => {
self.trace_as_string_buf.clear();
ty.trace_fmt_append_to_string(
&mut self.trace_as_string_buf,
OpaqueSimValueSlice::from_bitslice(&element_data[start..start + len]),
);
write_string_value_change(
&mut self.writer,
&self.trace_as_string_buf,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(first_id + element_index),
)?;
self.trace_as_string_buf.clear();
}
} }
} }
Ok(()) Ok(())
} }
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> { fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
write_bits_value_change( write_bits_value_change(&mut self.writer, value, id.as_usize())
&mut self.writer,
value,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
} }
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> { fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
write_bits_value_change( write_bits_value_change(&mut self.writer, value, id.as_usize())
&mut self.writer,
value,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
} }
fn finish_init(&mut self) -> Result<(), Self::Error> { fn finish_init(&mut self) -> Result<(), Self::Error> {
@ -1450,30 +1088,7 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
variant_index: usize, variant_index: usize,
ty: Enum, ty: Enum,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
write_enum_discriminant_value_change( write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize())
&mut self.writer,
variant_index,
ty,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(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:?}"),
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
} }
fn set_signal_sim_only_value( fn set_signal_sim_only_value(
@ -1481,23 +1096,7 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
id: TraceScalarId, id: TraceScalarId,
value: &DynSimOnlyValue, value: &DynSimOnlyValue,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
write_string_value_change( write_string_value_change(&mut self.writer, format_args!("{value:?}"), id.as_usize())
&mut self.writer,
format_args!("{value:?}"),
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
}
fn set_signal_string(&mut self, id: TraceScalarId, value: &str) -> Result<(), Self::Error> {
write_string_value_change(
&mut self.writer,
value,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
} }
} }
@ -1508,7 +1107,6 @@ impl<W: io::Write> fmt::Debug for VcdWriter<W> {
finished_init, finished_init,
timescale, timescale,
properties: _, properties: _,
trace_as_string_buf: _,
} = self; } = self;
f.debug_struct("VcdWriter") f.debug_struct("VcdWriter")
.field("finished_init", finished_init) .field("finished_init", finished_init)
@ -1523,7 +1121,7 @@ mod tests {
#[test] #[test]
fn test_scope() { fn test_scope() {
let mut scope = Scope::new(PathHash::default()); let mut scope = Scope::default();
assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo"); assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo");
assert_eq!( assert_eq!(
&*scope.new_identifier("foo_0".intern()).unescaped_name, &*scope.new_identifier("foo_0".intern()).unescaped_name,

View file

@ -1,56 +1,25 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
build::{ cli::{FormalArgs, FormalMode, FormalOutput, RunPhase},
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, firrtl::ExportOptions,
module::Module, util::HashMap,
sim::{Simulation, vcd::VcdWriterDecls},
util::{HashMap, RcWriter},
}; };
use serde::{Deserialize, Serialize}; use clap::Parser;
use serde::Deserialize;
use std::{ use std::{
fmt::{self, Write}, fmt::Write,
panic::Location,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
sync::{Mutex, OnceLock}, sync::{Mutex, OnceLock},
}; };
#[derive( fn assert_formal_helper() -> FormalArgs {
clap::ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default, Deserialize, Serialize, static FORMAL_ARGS: OnceLock<FormalArgs> = 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
#[non_exhaustive] FORMAL_ARGS
pub enum FormalMode { .get_or_init(|| FormalArgs::parse_from(["fayalite::testing::assert_formal"]))
#[default] .clone()
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)] #[derive(Deserialize)]
@ -128,286 +97,26 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf {
.join(dir) .join(dir)
} }
fn make_assert_formal_args(
test_name: &dyn std::fmt::Display,
formal_mode: FormalMode,
formal_depth: u64,
solver: Option<&str>,
export_options: ExportOptions,
) -> eyre::Result<JobArgsAndDependencies<ExternalCommandJobKind<Formal>>> {
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<M: AsRef<Module<T>>, 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] #[track_caller]
pub fn assert_formal<M: AsRef<Module<T>>, T: BundleType>( pub fn assert_formal<M>(
test_name: impl std::fmt::Display, test_name: impl std::fmt::Display,
module: M, module: M,
formal_mode: FormalMode, mode: FormalMode,
formal_depth: u64, depth: u64,
solver: Option<&str>, solver: Option<&str>,
export_options: ExportOptions, export_options: ExportOptions,
) { ) where
try_assert_formal( FormalArgs: RunPhase<M, Output = FormalOutput>,
test_name, {
module, let mut args = assert_formal_helper();
formal_mode, args.verilog.firrtl.base.redirect_output_for_rust_test = true;
formal_depth, args.verilog.firrtl.base.output = Some(get_assert_formal_target_path(&test_name));
solver, args.verilog.firrtl.export_options = export_options;
export_options, args.verilog.debug = true;
) args.mode = mode;
.expect("testing::assert_formal() failed"); args.depth = depth;
if let Some(solver) = solver {
args.solver = solver.into();
}
args.run(module).expect("testing::assert_formal() failed");
} }
pub struct CheckedVcdOutput {
writer: Option<RcWriter>,
expected_path: PathBuf,
expected_contents: Result<String, (Option<PathBuf>, std::io::Error)>,
location: &'static Location<'static>,
}
impl CheckedVcdOutput {
#[must_use]
#[track_caller]
pub fn new<T: BundleType>(sim: &mut Simulation<T>, expected_path: PathBuf) -> Self {
let writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
Self {
writer: Some(writer),
expected_contents: std::fs::read_to_string(&expected_path).map_err(|e| {
eprintln!(
"error: failed to read expected VCD from: {}",
expected_path.display(),
);
(std::env::current_dir().ok(), e)
}),
expected_path,
location: Location::caller(),
}
}
#[must_use]
#[track_caller]
#[doc(hidden)]
pub fn __checked_vcd_output_macro_helper<T: BundleType>(
sim: &mut Simulation<T>,
cargo_manifest_dir: &'static str,
path: &'static str,
) -> Self {
Self::new(sim, Path::new(cargo_manifest_dir).join(path))
}
pub fn with_vcd_output<R>(&self, f: impl FnOnce(&str) -> R) -> R {
let Some(writer) = &self.writer else {
unreachable!();
};
writer.clone().borrow(|output| {
let Ok(output) = str::from_utf8(output) else {
unreachable!("VcdWriter writes valid UTF-8");
};
f(output)
})
}
#[track_caller]
pub fn finish(mut self) {
let Ok(()) = self.finish_impl(|msg| panic!("{msg}"));
}
fn finish_impl<E>(
&mut self,
error: impl FnOnce(std::fmt::Arguments<'_>) -> E,
) -> Result<(), E> {
let Self {
writer: Some(writer),
expected_path,
expected_contents,
location,
} = self
else {
// already finished
return Ok(());
};
let Ok(vcd) = String::from_utf8(writer.take()) else {
unreachable!("VcdWriter writes valid UTF-8");
};
let expected_path_d = expected_path.display();
if expected_contents
.as_ref()
.is_ok_and(|expected_contents| *expected_contents == vcd)
{
// avoid written output from being split from threads interleaving writes to stdout
let _stdout = std::io::stderr().lock();
// use println to get output captured by tests
println!("\n{location}: generated VCD matches the expected VCD in {expected_path_d}");
return Ok(());
}
// avoid written output from being split from threads interleaving writes to stderr
let _stderr = std::io::stderr().lock();
let error = |msg: std::fmt::Arguments<'_>| {
// print msg at both beginning and end so it's easier to find when the vcd is huge
Err(error(format_args!(
"\n{msg}####### VCD:\n{vcd}\n#######\n{msg}"
)))
};
let error = |msg: std::fmt::Arguments<'_>| match &*expected_contents {
Ok(_) => error(format_args!(
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
{msg}",
)),
Err((Some(current_dir), e)) => error(format_args!(
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
error: failed to read: {e}\n\
current dir: {current_dir}\n\
{msg}",
current_dir = current_dir.display(),
)),
Err((None, e)) => error(format_args!(
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
error: failed to read: {e}\n\
{msg}",
)),
};
const OVERWRITE_VAR_NAME: &str = "OVERWRITE_EXPECTED_VCD";
const OVERWRITE_VAR_VALUE: &str = "overwrite";
match std::env::var_os(OVERWRITE_VAR_NAME) {
Some(v) if v == OVERWRITE_VAR_VALUE => match std::fs::write(&expected_path, &vcd) {
Ok(()) => error(format_args!(
"warning: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- writing the generated VCD to {expected_path_d}\n"
)),
Err(e) => error(format_args!(
"error: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- tried to write the generated VCD to {expected_path_d}\n\
error: failed to write: {e}"
)),
},
_ => error(format_args!(
"note: rerun the test with the environment variable `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}`\n\
to update the expected output to match the generated output.\n"
)),
}
}
}
impl Drop for CheckedVcdOutput {
#[track_caller]
fn drop(&mut self) {
let _ = self.finish_impl(|msg| {
if std::thread::panicking() {
eprintln!("{msg}"); // use eprintln to get output captured by tests
} else {
panic!("{msg}");
}
});
}
}
#[macro_export]
/// Use in tests to check that [`Simulation`] generates the expected VCD traces, by comparing to a `.vcd` file containing the expected traces.
///
/// Use like so:
/// ```
/// # use fayalite::prelude::*;
/// #
/// # #[hdl_module]
/// # fn my_module() {
/// # #[hdl]
/// # let a: UInt<8> = m.input();
/// # #[hdl]
/// # let b: UInt<8> = m.output();
/// # connect(b, 0u8);
/// # #[hdl]
/// # if a.cmp_eq(100u8) {
/// # connect(b, 42u8);
/// # }
/// # }
/// // inside your #[test] fn my_test():
///
/// // get the module to simulate:
/// let m = my_module();
/// // create a simulation of the module:
/// let mut sim = Simulation::new(m);
/// // set up the expected VCD traces, the given .vcd path is relative to env!("CARGO_MANIFEST_DIR")
/// let _checked_vcd_output = checked_vcd_output!(
/// &mut sim,
/// "tests/expected/my_test.vcd",
/// );
/// // now run the simulation like normal:
/// sim.write(sim.io().a, 0u8);
/// assert_eq!(sim.read(sim.io().b).as_int(), 0);
/// sim.advance_time(SimDuration::from_micros(1));
/// sim.write(sim.io().a, 100u8);
/// assert_eq!(sim.read(sim.io().b).as_int(), 42);
/// ```
macro_rules! checked_vcd_output {
($sim:expr, $path_relative_to_manifest_dir:expr $(,)?) => {
$crate::testing::CheckedVcdOutput::__checked_vcd_output_macro_helper(
$sim,
$crate::__std::env!("CARGO_MANIFEST_DIR"),
$crate::__std::concat!($path_relative_to_manifest_dir),
)
};
}
pub use checked_vcd_output;

File diff suppressed because it is too large Load diff

View file

@ -12,8 +12,7 @@ use crate::{
prelude::PhantomConst, prelude::PhantomConst,
reset::{AsyncReset, Reset, SyncReset}, reset::{AsyncReset, Reset, SyncReset},
sim::value::DynSimOnly, sim::value::DynSimOnly,
ty::{BaseType, CanonicalType, TraceAsString, TraceAsStringTrait}, ty::{BaseType, CanonicalType},
util::serde_by_id::SerdeById,
}; };
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -39,7 +38,6 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for SerdePhantomConst<
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename = "CanonicalType")] #[serde(rename = "CanonicalType")]
#[expect(private_interfaces)]
pub(crate) enum SerdeCanonicalType< pub(crate) enum SerdeCanonicalType<
ArrayElement = CanonicalType, ArrayElement = CanonicalType,
ThePhantomConst = SerdePhantomConst<Interned<PhantomConstCanonicalValue>>, ThePhantomConst = SerdePhantomConst<Interned<PhantomConstCanonicalValue>>,
@ -67,10 +65,6 @@ pub(crate) enum SerdeCanonicalType<
Clock, Clock,
PhantomConst(ThePhantomConst), PhantomConst(ThePhantomConst),
DynSimOnly(DynSimOnly), DynSimOnly(DynSimOnly),
TraceAsString {
inner_ty: Interned<CanonicalType>,
trace_as_string: SerdeById<Interned<dyn TraceAsStringTrait>>,
},
} }
impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> { impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> {
@ -88,7 +82,6 @@ impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomCo
Self::Clock => "a Clock", Self::Clock => "a Clock",
Self::PhantomConst(_) => "a PhantomConst", Self::PhantomConst(_) => "a PhantomConst",
Self::DynSimOnly(_) => "a SimOnlyValue", Self::DynSimOnly(_) => "a SimOnlyValue",
Self::TraceAsString { .. } => "a TraceAsString",
} }
} }
} }
@ -116,15 +109,6 @@ impl<T: BaseType> From<T> for SerdeCanonicalType {
CanonicalType::Clock(Clock {}) => Self::Clock, CanonicalType::Clock(Clock {}) => Self::Clock,
CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())), CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())),
CanonicalType::DynSimOnly(ty) => Self::DynSimOnly(ty), CanonicalType::DynSimOnly(ty) => Self::DynSimOnly(ty),
CanonicalType::TraceAsString(TraceAsString {
inner_ty,
trace_as_string,
}) => Self::TraceAsString {
inner_ty: inner_ty.interned(),
trace_as_string: SerdeById {
inner: trace_as_string.interned(),
},
},
} }
} }
} }
@ -143,16 +127,9 @@ impl From<SerdeCanonicalType> for CanonicalType {
SerdeCanonicalType::Reset => Self::Reset(Reset), SerdeCanonicalType::Reset => Self::Reset(Reset),
SerdeCanonicalType::Clock => Self::Clock(Clock), SerdeCanonicalType::Clock => Self::Clock(Clock),
SerdeCanonicalType::PhantomConst(value) => { SerdeCanonicalType::PhantomConst(value) => {
Self::PhantomConst(PhantomConst::new_interned(value.0)) Self::PhantomConst(PhantomConst::new(value.0))
} }
SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value), SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value),
SerdeCanonicalType::TraceAsString {
inner_ty,
trace_as_string,
} => Self::TraceAsString(TraceAsString {
inner_ty: crate::intern::LazyInterned::Interned(inner_ty),
trace_as_string: crate::intern::LazyInterned::Interned(trace_as_string.inner),
}),
} }
} }
} }

View file

@ -16,8 +16,8 @@ pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher;
#[cfg(not(feature = "unstable-test-hasher"))] #[cfg(not(feature = "unstable-test-hasher"))]
pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder; pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder;
pub(crate) type HashMap<K, V, H = DefaultBuildHasher> = hashbrown::HashMap<K, V, H>; pub(crate) type HashMap<K, V> = hashbrown::HashMap<K, V, DefaultBuildHasher>;
pub(crate) type HashSet<T, H = DefaultBuildHasher> = hashbrown::HashSet<T, H>; pub(crate) type HashSet<T> = hashbrown::HashSet<T, DefaultBuildHasher>;
#[doc(inline)] #[doc(inline)]
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
@ -33,21 +33,13 @@ pub use const_cmp::{
#[doc(inline)] #[doc(inline)]
pub use scoped_ref::ScopedRef; pub use scoped_ref::ScopedRef;
pub(crate) use misc::chain;
#[doc(inline)] #[doc(inline)]
pub use misc::{ pub use misc::{
BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, interned_bit,
SerdeJsonEscapeIf, SerdeJsonEscapeIfFormatter, SerdeJsonEscapeIfTest, iter_eq_by, slice_range, try_slice_range,
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, copy_le_bytes_to_bitslice};
pub mod bool_fixed_point_solver;
pub(crate) mod indented_print;
pub mod job_server; pub mod job_server;
pub mod map_trait;
pub mod prefix_sum; pub mod prefix_sum;
pub mod ready_valid; pub mod ready_valid;
pub(crate) mod serde_by_id;
pub mod union_find_map;

View file

@ -1,711 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use petgraph::unionfind::UnionFind;
use std::{collections::BTreeSet, fmt};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Variable(usize);
impl Variable {
pub fn index(self) -> usize {
self.0
}
}
impl fmt::Debug for Variable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for Variable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "v{}", self.0)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Constraint {
/// `variable` is constrained to be [`!solver.unconstrained_variables_value()`](BoolFixedPointSolver::unconstrained_variables_value())
MaximallyConstrained { variable: Variable },
/// the constraint is `dest == src`
Equal { dest: Variable, src: Variable },
/// the constraint is `dest == dest & src`
And { dest: Variable, src: Variable },
/// the constraint is `dest == dest | src`
Or { dest: Variable, src: Variable },
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
/// the constraint is `dest == dest & src`
struct AndConstraint {
dest: Variable,
src: Variable,
}
impl AndConstraint {
fn from_or_constraint(or_constraint_dest: Variable, or_constraint_src: Variable) -> Self {
// `a == a | b` is equivalent to `b == b & a`
Self {
dest: or_constraint_src,
src: or_constraint_dest,
}
}
}
impl fmt::Debug for AndConstraint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { dest, src } = *self;
write!(f, "{dest} == {dest} & {src}")
}
}
#[derive(Clone)]
pub struct BoolFixedPointSolver {
variables_union_find: UnionFind<usize>,
variables_value: Vec<bool>,
maximally_constrained: Vec<bool>,
unconstrained_variables_value: bool,
solved: bool,
and_constraints: BTreeSet<AndConstraint>,
}
impl fmt::Debug for BoolFixedPointSolver {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
variables_union_find,
variables_value,
maximally_constrained,
unconstrained_variables_value,
solved,
and_constraints,
} = self;
f.debug_struct("BoolFixedPointSolver")
.field(
"variables_union_find",
&fmt::from_fn(|f| {
f.debug_map()
.entries(
(0..variables_union_find.len())
.map(|i| (Variable(i), Variable(variables_union_find.find(i)))),
)
.finish()
}),
)
.field(
"variables_value",
&fmt::from_fn(|f| {
let mut debug_map = f.debug_map();
for (i, v) in variables_value.iter().enumerate() {
if variables_union_find.find(i) == i {
debug_map.entry(&Variable(i), v);
}
}
debug_map.finish()
}),
)
.field(
"maximally_constrained",
&fmt::from_fn(|f| {
let mut debug_map = f.debug_map();
for (i, v) in maximally_constrained.iter().enumerate() {
if variables_union_find.find(i) == i {
debug_map.entry(&Variable(i), v);
}
}
debug_map.finish()
}),
)
.field(
"unconstrained_variables_value",
unconstrained_variables_value,
)
.field("solved", solved)
.field("and_constraints", and_constraints)
.finish()
}
}
impl BoolFixedPointSolver {
pub const fn new(unconstrained_variables_value: bool) -> Self {
Self {
variables_union_find: UnionFind::new_empty(),
variables_value: Vec::new(),
maximally_constrained: Vec::new(),
unconstrained_variables_value,
solved: false,
and_constraints: BTreeSet::new(),
}
}
pub fn unconstrained_variables_value(&self) -> bool {
self.unconstrained_variables_value
}
pub fn new_variable(&mut self) -> Variable {
let index = self.variables_union_find.new_set();
self.variables_value
.push(self.unconstrained_variables_value);
self.maximally_constrained.push(false);
self.solved = false;
Variable(index)
}
pub fn variable_count(&self) -> usize {
self.variables_union_find.len()
}
#[track_caller]
fn assert_variable_in_range(&self, variable: Variable) {
if variable.0 >= self.variable_count() {
panic!("invalid variable {variable:?}");
}
}
#[track_caller]
pub fn add_constraint(&mut self, constraint: Constraint) {
self.solved = false;
match constraint {
Constraint::MaximallyConstrained { variable } => {
self.assert_variable_in_range(variable);
self.maximally_constrained[self.variables_union_find.find_mut(variable.0)] = true;
return;
}
Constraint::Equal { dest, src } => {
self.assert_variable_in_range(dest);
self.assert_variable_in_range(src);
let maximally_constrained = self.maximally_constrained
[self.variables_union_find.find_mut(dest.0)]
| self.maximally_constrained[self.variables_union_find.find_mut(src.0)];
self.variables_union_find.union(dest.0, src.0);
let merged_index = self.variables_union_find.find_mut(dest.0);
self.maximally_constrained[merged_index] = maximally_constrained;
}
Constraint::And { dest, src } => {
self.assert_variable_in_range(src);
self.assert_variable_in_range(dest);
if src != dest {
self.and_constraints.insert(AndConstraint { dest, src });
}
}
Constraint::Or { dest, src } => {
self.assert_variable_in_range(src);
self.assert_variable_in_range(dest);
if src != dest {
self.and_constraints
.insert(AndConstraint::from_or_constraint(dest, src));
}
}
}
}
pub fn solve(&mut self) {
for (value, maximally_constrained) in self
.variables_value
.iter_mut()
.zip(&self.maximally_constrained)
{
*value = self.unconstrained_variables_value ^ *maximally_constrained;
}
let mut variables_to_constraints_map: Vec<Vec<AndConstraint>> =
vec![Vec::new(); self.variable_count()];
for &AndConstraint { mut dest, mut src } in &self.and_constraints {
dest.0 = self.variables_union_find.find_mut(dest.0);
src.0 = self.variables_union_find.find_mut(src.0);
if dest == src {
continue;
}
let constraint = AndConstraint { dest, src };
variables_to_constraints_map[dest.0].push(constraint);
variables_to_constraints_map[src.0].push(constraint);
}
let mut worklist: Vec<Variable> = (0..self.variable_count())
.filter(|&index| self.variables_union_find.find_mut(index) == index)
.map(Variable)
.collect();
while let Some(variable) = worklist.pop() {
for &AndConstraint { dest, src } in &variables_to_constraints_map[variable.0] {
let dest_value = self.variables_value[dest.0];
let src_value = self.variables_value[src.0];
// equivalent to `dest_value != dest_value & src_value`:
let is_unsatisfied = dest_value && !src_value;
if is_unsatisfied {
if self.unconstrained_variables_value {
self.variables_value[dest.0] = false;
worklist.push(dest);
} else {
self.variables_value[src.0] = true;
worklist.push(src);
}
}
}
}
self.solved = true;
}
#[track_caller]
pub fn value(&mut self, variable: Variable) -> bool {
#[cold]
fn solve_cold(this: &mut BoolFixedPointSolver) {
this.solve();
}
self.assert_variable_in_range(variable);
if !self.solved {
solve_cold(self);
}
self.variables_value[self.variables_union_find.find_mut(variable.0)]
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::num::NonZero;
struct TestCase<'a, C, Vars, Vals> {
variable_count: usize,
expected_values: Option<&'a [bool]>,
constraints: C,
variables: Vars,
values: Vals,
solver: BoolFixedPointSolver,
}
impl<'a, C: FnOnce(&[Variable]) -> I, I: IntoIterator<Item = Constraint>> TestCase<'a, C, (), ()> {
fn new_expected(
unconstrained_variables_value: bool,
expected_values: &'a [bool],
constraints: C,
) -> Self {
Self {
variable_count: expected_values.len(),
expected_values: Some(expected_values),
constraints,
variables: (),
values: (),
solver: BoolFixedPointSolver::new(unconstrained_variables_value),
}
}
#[track_caller]
fn get_constraints_and_variables(
self,
) -> TestCase<'a, Vec<Constraint>, Vec<Variable>, [bool; 0]> {
let Self {
variable_count,
expected_values,
constraints,
variables: (),
values: (),
mut solver,
} = self;
assert_eq!(
expected_values.map_or(variable_count, |v| v.len()),
variable_count,
);
let variables = Vec::from_iter((0..variable_count).map(|_| solver.new_variable()));
let constraints = Vec::from_iter(constraints(&variables));
TestCase {
variable_count,
expected_values,
constraints,
variables,
values: [],
solver,
}
}
}
impl<'a> TestCase<'a, Vec<Constraint>, Vec<Variable>, [bool; 0]> {
#[track_caller]
fn add_and_check_constraints(&mut self) {
if let Some(expected_values) = self.expected_values {
self.check_constraints("expected values", expected_values);
}
for &constraint in &self.constraints {
self.solver.add_constraint(constraint);
}
}
#[track_caller]
fn get_values(self) -> TestCase<'a, Vec<Constraint>, Vec<Variable>, Vec<bool>> {
let Self {
variable_count,
expected_values,
constraints,
variables,
values: [],
mut solver,
} = self;
let values = Vec::from_iter(variables.iter().map(|&v| solver.value(v)));
TestCase {
variable_count,
expected_values,
constraints,
variables,
values,
solver,
}
}
}
impl<'a> TestCase<'a, Vec<Constraint>, Vec<Variable>, Vec<bool>> {
#[track_caller]
fn check_values(&self) {
let Self {
variable_count: _,
expected_values,
constraints: _,
variables,
values,
solver: _,
} = self;
if let Some(expected_values) = expected_values {
for ((&expected_value, &variable), &value) in
expected_values.iter().zip(variables).zip(values)
{
if expected_value != value {
self.error(format_args!(
"solver output for {variable} of {value:?} doesn't \
match expected value of {expected_value:?}",
));
}
}
}
self.check_constraints("solved values", values);
}
}
impl<'a, Vals: AsRef<[bool]>> TestCase<'a, Vec<Constraint>, Vec<Variable>, Vals> {
#[track_caller]
fn check_constraints(&self, values_name: &str, values: &[bool]) {
let unconstrained_variables_value = self.solver.unconstrained_variables_value();
let v = |variable: Variable| values[variable.index()];
for &constraint in &self.constraints {
let satisfied = match constraint {
Constraint::MaximallyConstrained { variable } => {
v(variable) != unconstrained_variables_value
}
Constraint::Equal { dest, src } => v(dest) == v(src),
Constraint::And { dest, src } => v(dest) == v(dest) & v(src),
Constraint::Or { dest, src } => v(dest) == v(dest) | v(src),
};
if !satisfied {
self.error(format_args!(
"{values_name} don't satisfy constraint: {constraint:#?}"
));
}
}
}
#[track_caller]
fn error(&self, msg: fmt::Arguments<'_>) -> ! {
let Self {
variable_count,
expected_values,
ref constraints,
ref variables,
ref values,
ref solver,
} = *self;
let values = values.as_ref();
panic!(
"{msg}\n\
values={values:#?}\n\
constraints={constraints:#?}\n\
solver={solver:#?}",
values = fmt::from_fn(|f| {
let mut debug_map = f.debug_map();
for i in 0..variable_count {
debug_map.key(&variables[i]);
if let Some(value) = values.get(i) {
if let Some(expected_values) = expected_values {
debug_map.value(&format_args!(
"{value:?} (expected: {:?})",
expected_values[i],
));
} else {
debug_map.value(value);
}
} else if let Some(expected_values) = expected_values {
debug_map.value(&format_args!("(expected: {:?})", expected_values[i]));
} else {
debug_map.value(&format_args!("None"));
}
}
debug_map.finish()
}),
);
}
}
#[track_caller]
fn test_case<I: IntoIterator<Item = Constraint>>(
test_case: TestCase<'_, impl FnOnce(&[Variable]) -> I, (), ()>,
) {
let mut test_case = test_case.get_constraints_and_variables();
test_case.add_and_check_constraints();
let test_case = test_case.get_values();
test_case.check_values();
}
#[test]
fn test_bool_fixed_point_solver_simple() {
test_case(TestCase::new_expected(false, &[], |_| []));
test_case(TestCase::new_expected(true, &[], |_| []));
test_case(TestCase::new_expected(false, &[false], |_| []));
test_case(TestCase::new_expected(true, &[true], |_| []));
test_case(TestCase::new_expected(false, &[true], |v| {
[Constraint::MaximallyConstrained { variable: v[0] }]
}));
test_case(TestCase::new_expected(true, &[false], |v| {
[Constraint::MaximallyConstrained { variable: v[0] }]
}));
test_case(TestCase::new_expected(false, &[true, true], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::Equal {
dest: v[1],
src: v[0],
},
]
}));
test_case(TestCase::new_expected(true, &[false, false], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::Equal {
dest: v[1],
src: v[0],
},
]
}));
test_case(TestCase::new_expected(false, &[true, false], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::And {
dest: v[1],
src: v[0],
},
]
}));
test_case(TestCase::new_expected(true, &[false, false], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::And {
dest: v[1],
src: v[0],
},
]
}));
test_case(TestCase::new_expected(false, &[true, true], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::And {
dest: v[0],
src: v[1],
},
]
}));
test_case(TestCase::new_expected(true, &[false, true], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::And {
dest: v[0],
src: v[1],
},
]
}));
test_case(TestCase::new_expected(false, &[true, true], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::Or {
dest: v[1],
src: v[0],
},
]
}));
test_case(TestCase::new_expected(true, &[false, true], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::Or {
dest: v[1],
src: v[0],
},
]
}));
test_case(TestCase::new_expected(false, &[true, false], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::Or {
dest: v[0],
src: v[1],
},
]
}));
test_case(TestCase::new_expected(true, &[false, false], |v| {
[
Constraint::MaximallyConstrained { variable: v[0] },
Constraint::Or {
dest: v[0],
src: v[1],
},
]
}));
}
#[derive(Debug)]
struct Rng {
state: u64,
}
impl Rng {
fn new(test_case_index: u32) -> Self {
Self {
state: (test_case_index as u64) << 32,
}
}
fn next_u64(&mut self) -> u64 {
self.state += 1;
// 4 random primes and 4 random rotate amounts
self.state
.wrapping_mul(0xA3C7_8807_EA6D_A4F9)
.rotate_left(43)
.wrapping_mul(0x1CCA_797A_6BF8_8C63)
.rotate_left(8)
.wrapping_mul(0xCC50_AA59_7C41_946F)
.rotate_left(12)
.wrapping_mul(0xFB2A_0137_F878_C4B5)
.rotate_left(58)
}
#[track_caller]
fn next_u64_in_range(&mut self, range: std::ops::Range<u64>) -> u64 {
let Some(len) = range.end.checked_sub(range.start).and_then(NonZero::new) else {
panic!("empty range: {range:?}");
};
let max_quotient = u64::MAX / len;
loop {
let next_u64 = self.next_u64();
let quotient = next_u64 / len;
let remainder = next_u64 % len;
if quotient < max_quotient {
return remainder + range.start;
}
}
}
#[track_caller]
fn next_usize_in_range(&mut self, range: std::ops::Range<usize>) -> usize {
self.next_u64_in_range(range.start as u64..range.end as u64) as usize
}
#[track_caller]
fn next_from_slice<'a, T>(&mut self, slice: &'a [T]) -> &'a T {
assert!(!slice.is_empty());
&slice[self.next_usize_in_range(0..slice.len())]
}
fn next_bool(&mut self) -> bool {
(self.next_u64() & 1) != 0
}
}
#[track_caller]
fn test_bool_fixed_point_solver_random_case(test_case_index: u32) {
println!("test_bool_fixed_point_solver_random_case({test_case_index})");
let mut rng = Rng::new(test_case_index);
// bias towards smaller problems to make them easier to debug
let variable_count = rng
.next_u64_in_range(1..1_000_000)
.pow(2)
.div_ceil(1_000_000_000) as usize;
let constraint_count =
rng.next_usize_in_range(0..(variable_count * variable_count).clamp(0, 10000));
let solver = BoolFixedPointSolver::new(rng.next_bool());
test_case(TestCase {
variable_count,
expected_values: None,
constraints: |variables: &[Variable]| {
Vec::from_iter(
(0..constraint_count).map(|_| match rng.next_usize_in_range(0..4) {
0 => Constraint::MaximallyConstrained {
variable: *rng.next_from_slice(variables),
},
1 => Constraint::Equal {
dest: *rng.next_from_slice(variables),
src: *rng.next_from_slice(variables),
},
2 => Constraint::And {
dest: *rng.next_from_slice(variables),
src: *rng.next_from_slice(variables),
},
3 => Constraint::Or {
dest: *rng.next_from_slice(variables),
src: *rng.next_from_slice(variables),
},
4.. => unreachable!(),
}),
)
},
variables: (),
values: (),
solver,
});
}
const CASES_FULL_RANGE: std::ops::Range<u32> = 0..100_000;
fn mul_div(v: u32, factor: u32, divisor: u32) -> u32 {
((v as u64 * factor as u64) / divisor as u64) as u32
}
#[track_caller]
fn test_bool_fixed_point_solver_random_cases(split_index: u32) {
assert!(split_index < CASES_SPLIT_COUNT);
let full_range_len = CASES_FULL_RANGE.end - CASES_FULL_RANGE.start;
let start = mul_div(split_index, full_range_len, CASES_SPLIT_COUNT);
let end = mul_div(split_index + 1, full_range_len, CASES_SPLIT_COUNT);
for test_case_index in start..end {
test_bool_fixed_point_solver_random_case(test_case_index)
}
}
const CASES_SPLIT_COUNT: u32 = 10;
#[test]
fn test_bool_fixed_point_solver_random_cases_0() {
test_bool_fixed_point_solver_random_cases(0);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_1() {
test_bool_fixed_point_solver_random_cases(1);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_2() {
test_bool_fixed_point_solver_random_cases(2);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_3() {
test_bool_fixed_point_solver_random_cases(3);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_4() {
test_bool_fixed_point_solver_random_cases(4);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_5() {
test_bool_fixed_point_solver_random_cases(5);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_6() {
test_bool_fixed_point_solver_random_cases(6);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_7() {
test_bool_fixed_point_solver_random_cases(7);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_8() {
test_bool_fixed_point_solver_random_cases(8);
}
#[test]
fn test_bool_fixed_point_solver_random_cases_9() {
test_bool_fixed_point_solver_random_cases(9);
}
}

View file

@ -1,117 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::{
fmt::{self, Write as _},
marker::PhantomData,
};
struct IndentState {
indent: usize,
need_indent: bool,
buf: String,
}
thread_local! {
static INDENT_STATE: std::cell::RefCell<IndentState> = const {
std::cell::RefCell::new(IndentState {
indent: 0,
need_indent: true,
buf: String::new(),
})
};
}
struct IndentedOut;
impl fmt::Write for IndentedOut {
fn write_str(&mut self, s: &str) -> fmt::Result {
INDENT_STATE.with_borrow_mut(|state| {
let IndentState {
indent,
need_indent,
buf,
} = state;
buf.clear();
for ch in s.chars() {
if ch == '\n' {
*need_indent = true;
} else {
if *need_indent {
*need_indent = false;
for _ in 0..*indent {
buf.push_str(" ");
}
}
}
buf.push(ch)
}
std::print!("{buf}");
});
Ok(())
}
}
#[allow(unused)]
pub(crate) struct PushIndent(PhantomData<*const ()>);
impl Drop for PushIndent {
fn drop(&mut self) {
let _ = INDENT_STATE.try_with(|state| state.borrow_mut().indent -= 1);
}
}
impl PushIndent {
#[allow(unused)]
pub(crate) fn new() -> Self {
INDENT_STATE.with_borrow_mut(|state| state.indent += 1);
Self(PhantomData)
}
}
#[allow(unused)]
pub(crate) fn indented_print_fmt<const LN: bool>(args: fmt::Arguments<'_>) {
if LN {
writeln!(IndentedOut, "{args}").expect("writing can't fail")
} else {
IndentedOut.write_fmt(args).expect("writing can't fail")
}
}
#[allow(unused)]
macro_rules! indented_print {
($($args:tt)*) => {
$crate::util::indented_print::indented_print_fmt::<false>($crate::__std::format_args!($($args)*))
};
}
#[allow(unused)]
pub(crate) use indented_print;
#[allow(unused)]
macro_rules! indented_println {
($($args:tt)*) => {
$crate::util::indented_print::indented_print_fmt::<true>($crate::__std::format_args!($($args)*))
};
}
#[allow(unused)]
pub(crate) use indented_println;
#[allow(unused)]
macro_rules! indented_dbg {
($expr:expr) => {{
let v = $expr;
$crate::util::indented_print::indented_println!(
"[{}:{}:{}] {} = {v:#?}",
file!(),
line!(),
column!(),
stringify!($expr),
);
v
}};
}
#[allow(unused)]
pub(crate) use indented_dbg;

View file

@ -1,36 +1,26 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use ctor::{ctor, dtor}; use ctor::ctor;
use jobslot::Client; use jobslot::{Acquired, Client};
use std::{ use std::{
ffi::OsString, ffi::OsString,
io, mem, mem,
num::NonZeroUsize, num::NonZeroUsize,
sync::{Mutex, MutexGuard}, sync::{Condvar, Mutex, Once, OnceLock},
thread::spawn,
}; };
#[ctor] fn get_or_make_client() -> &'static Client {
static CLIENT: Mutex<Option<Option<Client>>> = unsafe { Mutex::new(Some(Client::from_env())) }; #[ctor]
static CLIENT: OnceLock<Client> = unsafe {
#[dtor] match Client::from_env() {
fn drop_client() { Some(client) => OnceLock::from(client),
drop( None => OnceLock::new(),
match CLIENT.lock() {
Ok(v) => v,
Err(e) => e.into_inner(),
} }
.take(), };
);
}
fn get_or_make_client() -> Client { CLIENT.get_or_init(|| {
CLIENT
.lock()
.expect("shouldn't have panicked")
.as_mut()
.expect("shutting down")
.get_or_insert_with(|| {
let mut available_parallelism = None; let mut available_parallelism = None;
let mut args = std::env::args_os().skip(1); let mut args = std::env::args_os().skip(1);
while let Some(arg) = args.next() { while let Some(arg) = args.next() {
@ -62,95 +52,141 @@ fn get_or_make_client() -> Client {
} else { } else {
NonZeroUsize::new(1).unwrap() NonZeroUsize::new(1).unwrap()
}; };
Client::new_with_fifo(available_parallelism.get() - 1) Client::new_with_fifo(available_parallelism.get() - 1).expect("failed to create job server")
.expect("failed to create job server")
}) })
.clone()
} }
struct State { struct State {
obtained_count: usize,
waiting_count: usize, waiting_count: usize,
available: Vec<Acquired>,
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<State> = Mutex::new(State { static STATE: Mutex<State> = Mutex::new(State {
obtained_count: 0,
waiting_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)] #[derive(Debug)]
pub struct AcquiredJob { pub struct AcquiredJob {
client: Client, job: AcquiredJobInner,
} }
impl AcquiredJob { impl AcquiredJob {
pub fn acquire() -> io::Result<Self> { 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(); 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(); let mut state = STATE.lock().unwrap();
state.waiting_count -= 1; 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 state
} else {
drop(state);
acquired = Some(
client
.acquire()
.expect("can't acquire token from job server"),
);
STATE.lock().unwrap()
};
} }
});
});
} }
impl Drop for Waiting { fn acquire_inner(block: bool) -> Option<Self> {
fn drop(&mut self) { Self::start_acquire_thread();
STATE.lock().unwrap().waiting_count -= 1;
}
}
let mut state = STATE.lock().unwrap(); let mut state = STATE.lock().unwrap();
if state.obtained_count == 0 && state.waiting_count == 0 { loop {
state.obtained_count = 1; // get implicit token if let Some(acquired) = state.available.pop() {
return Ok(Self { client }); return Some(Self {
job: AcquiredJobInner::FromJobServer(acquired),
});
}
if state.implicit_available {
state.implicit_available = false;
return Some(Self {
job: AcquiredJobInner::ImplicitJob,
});
}
if !block {
return None;
} }
state.waiting_count += 1; state.waiting_count += 1;
drop(state); state = COND_VAR.wait(state).unwrap();
let waiting = Waiting {}; state.waiting_count -= 1;
client.acquire_raw()?; }
state = waiting.done(); }
state.obtained_count = state pub fn try_acquire() -> Option<Self> {
.obtained_count Self::acquire_inner(false)
.checked_add(1) }
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "obtained_count overflowed"))?; pub fn acquire() -> Self {
drop(state); Self::acquire_inner(true).expect("failed to acquire token")
Ok(Self { client })
} }
pub fn run_command<R>( pub fn run_command<R>(
&mut self, &mut self,
cmd: std::process::Command, cmd: std::process::Command,
f: impl FnOnce(&mut std::process::Command) -> std::io::Result<R>, f: impl FnOnce(&mut std::process::Command) -> std::io::Result<R>,
) -> std::io::Result<R> { ) -> std::io::Result<R> {
self.client.configure_make_and_run_with_fifo(cmd, f) get_or_make_client().configure_make_and_run_with_fifo(cmd, f)
} }
} }
impl Drop for AcquiredJob { impl Drop for AcquiredJob {
fn drop(&mut self) { fn drop(&mut self) {
let mut state = STATE.lock().unwrap(); let mut state = STATE.lock().unwrap();
match &mut *state { match &self.job {
State { AcquiredJobInner::FromJobServer(_) => {
obtained_count: 0, .. if state.waiting_count > state.available.len() + state.implicit_available as usize {
} => unreachable!(), // allocate space before moving Acquired to ensure we
State { // drop Acquired outside of the lock on panic
obtained_count: obtained_count @ 1, state.available.reserve(1);
waiting_count, let AcquiredJobInner::FromJobServer(acquired) =
} => { mem::replace(&mut self.job, AcquiredJobInner::ImplicitJob)
*obtained_count = 0; // drop implicit token else {
let any_waiting = *waiting_count != 0; unreachable!()
drop(state); };
if any_waiting { state.available.push(acquired);
// we have the implicit token, but some other thread is trying to acquire a token, COND_VAR.notify_all();
// 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
} }
} }
State { obtained_count, .. } => { AcquiredJobInner::ImplicitJob => {
*obtained_count = obtained_count.saturating_sub(1); state.implicit_available = true;
drop(state); if state.waiting_count > state.available.len() {
let _ = self.client.release_raw(); // we're in drop, just ignore errors since we at least tried COND_VAR.notify_all();
}
} }
} }
} }

View file

@ -1,463 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::fmt;
pub enum Entry<'a, M: Map + 'a> {
Vacant(M::VacantEntry<'a>),
Occupied(M::OccupiedEntry<'a>),
}
impl<'a, M: Map + 'a> Entry<'a, M> {
pub fn and_modify<F: FnOnce(&mut M::Value)>(mut self, f: F) -> Self {
if let Self::Occupied(entry) = &mut self {
f(entry.get_mut());
}
self
}
pub fn insert_entry(self, v: M::Value) -> M::OccupiedEntry<'a> {
match self {
Self::Vacant(entry) => entry.insert_entry(v),
Self::Occupied(mut entry) => {
entry.insert(v);
entry
}
}
}
pub fn key(&self) -> &M::Key {
match self {
Self::Vacant(entry) => entry.key(),
Self::Occupied(entry) => entry.key(),
}
}
pub fn or_default(self) -> &'a mut M::Value
where
M::Value: Default,
{
self.or_insert_with(Default::default)
}
pub fn or_insert(self, v: M::Value) -> &'a mut M::Value {
match self {
Self::Vacant(entry) => entry.insert(v),
Self::Occupied(entry) => entry.into_mut(),
}
}
pub fn or_insert_with<F: FnOnce() -> M::Value>(self, f: F) -> &'a mut M::Value {
match self {
Self::Vacant(entry) => entry.insert(f()),
Self::Occupied(entry) => entry.into_mut(),
}
}
pub fn or_insert_with_key<F: FnOnce(&M::Key) -> M::Value>(self, f: F) -> &'a mut M::Value {
match self {
Self::Vacant(entry) => {
let v = f(entry.key());
entry.insert(v)
}
Self::Occupied(entry) => entry.into_mut(),
}
}
}
impl<'a, M: Map<OccupiedEntry<'a>: fmt::Debug, VacantEntry<'a>: fmt::Debug> + 'a> fmt::Debug
for Entry<'a, M>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Vacant(v) => f.debug_tuple("Vacant").field(v).finish(),
Self::Occupied(v) => f.debug_tuple("Occupied").field(v).finish(),
}
}
}
pub trait VacantEntry<'a>: Sized {
type Map: Map<VacantEntry<'a> = Self> + 'a;
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value;
fn insert_entry(self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::OccupiedEntry<'a>;
fn into_key(self) -> <Self::Map as Map>::Key;
fn key(&self) -> &<Self::Map as Map>::Key;
}
pub trait OccupiedEntry<'a>: Sized {
type Map: Map<OccupiedEntry<'a> = Self> + 'a;
fn get(&self) -> &<Self::Map as Map>::Value;
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value;
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value;
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value;
fn key(&self) -> &<Self::Map as Map>::Key;
fn remove(self) -> <Self::Map as Map>::Value;
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value);
}
pub trait Map:
Sized
+ IntoIterator<Item = (<Self as Map>::Key, <Self as Map>::Value)>
+ Extend<(<Self as Map>::Key, <Self as Map>::Value)>
+ FromIterator<(<Self as Map>::Key, <Self as Map>::Value)>
{
type Key;
type Value;
type IntoKeys: Iterator<Item = Self::Key>;
type IntoValues: Iterator<Item = Self::Value>;
type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)>
where
Self: 'a,
Self::Key: 'a,
Self::Value: 'a;
type IterMut<'a>: Iterator<Item = (&'a Self::Key, &'a mut Self::Value)>
where
Self: 'a,
Self::Key: 'a,
Self::Value: 'a;
type Keys<'a>: Iterator<Item = &'a Self::Key>
where
Self: 'a,
Self::Key: 'a;
type Values<'a>: Iterator<Item = &'a Self::Value>
where
Self: 'a,
Self::Value: 'a;
type ValuesMut<'a>: Iterator<Item = &'a mut Self::Value>
where
Self: 'a,
Self::Value: 'a;
type OccupiedEntry<'a>: OccupiedEntry<'a, Map = Self>
where
Self: 'a;
type VacantEntry<'a>: VacantEntry<'a, Map = Self>
where
Self: 'a;
fn clear(&mut self);
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self>;
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value>;
fn into_keys(self) -> Self::IntoKeys;
fn into_values(self) -> Self::IntoValues;
fn is_empty(&self) -> bool;
fn iter(&self) -> Self::Iter<'_>;
fn iter_mut(&mut self) -> Self::IterMut<'_>;
fn keys(&self) -> Self::Keys<'_>;
fn len(&self) -> usize;
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F);
fn values(&self) -> Self::Values<'_>;
fn values_mut(&mut self) -> Self::ValuesMut<'_>;
}
pub trait MapGet<Q: ?Sized>: Map {
fn contains_key(&self, k: &Q) -> bool;
fn get(&self, k: &Q) -> Option<&Self::Value>;
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value>;
fn remove(&mut self, k: &Q) -> Option<Self::Value>;
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)>;
}
mod hash_map {
use super::*;
use crate::util::HashMap;
use hashbrown::{Equivalent, hash_map};
use std::hash::{BuildHasher, Hash};
impl<K: Eq + Hash, V, H: BuildHasher + Default> Map for HashMap<K, V, H> {
type Key = K;
type Value = V;
type IntoKeys = hash_map::IntoKeys<K, V>;
type IntoValues = hash_map::IntoValues<K, V>;
type Iter<'a>
= hash_map::Iter<'a, K, V>
where
Self: 'a,
Self::Key: 'a,
Self::Value: 'a;
type IterMut<'a>
= hash_map::IterMut<'a, K, V>
where
Self: 'a,
Self::Key: 'a,
Self::Value: 'a;
type Keys<'a>
= hash_map::Keys<'a, K, V>
where
Self: 'a,
Self::Key: 'a;
type Values<'a>
= hash_map::Values<'a, K, V>
where
Self: 'a,
Self::Value: 'a;
type ValuesMut<'a>
= hash_map::ValuesMut<'a, K, V>
where
Self: 'a,
Self::Value: 'a;
type OccupiedEntry<'a>
= hash_map::OccupiedEntry<'a, K, V, H>
where
Self: 'a;
type VacantEntry<'a>
= hash_map::VacantEntry<'a, K, V, H>
where
Self: 'a;
fn clear(&mut self) {
self.clear();
}
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self> {
use hash_map::Entry::*;
match self.entry(k) {
Occupied(entry) => Entry::Occupied(entry),
Vacant(entry) => Entry::Vacant(entry),
}
}
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value> {
self.insert(k, v)
}
fn into_keys(self) -> Self::IntoKeys {
self.into_keys()
}
fn into_values(self) -> Self::IntoValues {
self.into_values()
}
fn is_empty(&self) -> bool {
self.is_empty()
}
fn iter(&self) -> Self::Iter<'_> {
self.iter()
}
fn iter_mut(&mut self) -> Self::IterMut<'_> {
self.iter_mut()
}
fn keys(&self) -> Self::Keys<'_> {
self.keys()
}
fn len(&self) -> usize {
self.len()
}
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F) {
self.retain(f);
}
fn values(&self) -> Self::Values<'_> {
self.values()
}
fn values_mut(&mut self) -> Self::ValuesMut<'_> {
self.values_mut()
}
}
impl<K: Eq + Hash, V, H: BuildHasher + Default, Q: ?Sized + Hash + Equivalent<K>> MapGet<Q>
for HashMap<K, V, H>
{
fn contains_key(&self, k: &Q) -> bool {
self.contains_key(k)
}
fn get(&self, k: &Q) -> Option<&Self::Value> {
self.get(k)
}
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value> {
self.get_mut(k)
}
fn remove(&mut self, k: &Q) -> Option<Self::Value> {
self.remove(k)
}
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)> {
self.remove_entry(k)
}
}
impl<'a, K: Eq + Hash, V, H: BuildHasher + Default> VacantEntry<'a>
for hash_map::VacantEntry<'a, K, V, H>
{
type Map = HashMap<K, V, H>;
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value {
self.insert(v)
}
fn insert_entry(
self,
v: <Self::Map as Map>::Value,
) -> <Self::Map as Map>::OccupiedEntry<'a> {
self.insert_entry(v)
}
fn into_key(self) -> <Self::Map as Map>::Key {
self.into_key()
}
fn key(&self) -> &<Self::Map as Map>::Key {
self.key()
}
}
impl<'a, K: Eq + Hash, V, H: BuildHasher + Default> OccupiedEntry<'a>
for hash_map::OccupiedEntry<'a, K, V, H>
{
type Map = HashMap<K, V, H>;
fn get(&self) -> &<Self::Map as Map>::Value {
self.get()
}
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value {
self.get_mut()
}
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value {
self.insert(v)
}
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value {
self.into_mut()
}
fn key(&self) -> &<Self::Map as Map>::Key {
self.key()
}
fn remove(self) -> <Self::Map as Map>::Value {
self.remove()
}
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value) {
self.remove_entry()
}
}
}
mod btree_map {
use super::*;
use std::collections::{BTreeMap, btree_map};
impl<K: Ord, V> Map for BTreeMap<K, V> {
type Key = K;
type Value = V;
type IntoKeys = btree_map::IntoKeys<K, V>;
type IntoValues = btree_map::IntoValues<K, V>;
type Iter<'a>
= btree_map::Iter<'a, K, V>
where
Self: 'a,
Self::Key: 'a,
Self::Value: 'a;
type IterMut<'a>
= btree_map::IterMut<'a, K, V>
where
Self: 'a,
Self::Key: 'a,
Self::Value: 'a;
type Keys<'a>
= btree_map::Keys<'a, K, V>
where
Self: 'a,
Self::Key: 'a;
type Values<'a>
= btree_map::Values<'a, K, V>
where
Self: 'a,
Self::Value: 'a;
type ValuesMut<'a>
= btree_map::ValuesMut<'a, K, V>
where
Self: 'a,
Self::Value: 'a;
type OccupiedEntry<'a>
= btree_map::OccupiedEntry<'a, K, V>
where
Self: 'a;
type VacantEntry<'a>
= btree_map::VacantEntry<'a, K, V>
where
Self: 'a;
fn clear(&mut self) {
self.clear();
}
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self> {
use btree_map::Entry::*;
match self.entry(k) {
Occupied(entry) => Entry::Occupied(entry),
Vacant(entry) => Entry::Vacant(entry),
}
}
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value> {
self.insert(k, v)
}
fn into_keys(self) -> Self::IntoKeys {
self.into_keys()
}
fn into_values(self) -> Self::IntoValues {
self.into_values()
}
fn is_empty(&self) -> bool {
self.is_empty()
}
fn iter(&self) -> Self::Iter<'_> {
self.iter()
}
fn iter_mut(&mut self) -> Self::IterMut<'_> {
self.iter_mut()
}
fn keys(&self) -> Self::Keys<'_> {
self.keys()
}
fn len(&self) -> usize {
self.len()
}
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F) {
self.retain(f);
}
fn values(&self) -> Self::Values<'_> {
self.values()
}
fn values_mut(&mut self) -> Self::ValuesMut<'_> {
self.values_mut()
}
}
impl<K: Ord + std::borrow::Borrow<Q>, V, Q: ?Sized + Ord> MapGet<Q> for BTreeMap<K, V> {
fn contains_key(&self, k: &Q) -> bool {
self.contains_key(k)
}
fn get(&self, k: &Q) -> Option<&Self::Value> {
self.get(k)
}
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value> {
self.get_mut(k)
}
fn remove(&mut self, k: &Q) -> Option<Self::Value> {
self.remove(k)
}
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)> {
self.remove_entry(k)
}
}
impl<'a, K: Ord, V> VacantEntry<'a> for btree_map::VacantEntry<'a, K, V> {
type Map = BTreeMap<K, V>;
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value {
self.insert(v)
}
fn insert_entry(
self,
v: <Self::Map as Map>::Value,
) -> <Self::Map as Map>::OccupiedEntry<'a> {
self.insert_entry(v)
}
fn into_key(self) -> <Self::Map as Map>::Key {
self.into_key()
}
fn key(&self) -> &<Self::Map as Map>::Key {
self.key()
}
}
impl<'a, K: Ord, V> OccupiedEntry<'a> for btree_map::OccupiedEntry<'a, K, V> {
type Map = BTreeMap<K, V>;
fn get(&self) -> &<Self::Map as Map>::Value {
self.get()
}
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value {
self.get_mut()
}
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value {
self.insert(v)
}
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value {
self.into_mut()
}
fn key(&self) -> &<Self::Map as Map>::Key {
self.key()
}
fn remove(self) -> <Self::Map as Map>::Value {
self.remove()
}
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value) {
self.remove_entry()
}
}
}

View file

@ -4,9 +4,7 @@ use crate::intern::{Intern, Interned};
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView}; use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
use std::{ use std::{
cell::Cell, cell::Cell,
ffi::OsStr,
fmt::{self, Debug, Write}, fmt::{self, Debug, Write},
io,
ops::{Bound, Range, RangeBounds}, ops::{Bound, Range, RangeBounds},
rc::Rc, rc::Rc,
sync::{Arc, OnceLock}, sync::{Arc, OnceLock},
@ -245,410 +243,3 @@ pub fn try_slice_range<R: RangeBounds<usize>>(range: R, size: usize) -> Option<R
pub fn slice_range<R: RangeBounds<usize>>(range: R, size: usize) -> Range<usize> { pub fn slice_range<R: RangeBounds<usize>>(range: R, size: usize) -> Range<usize> {
try_slice_range(range, size).expect("range out of bounds") try_slice_range(range, size).expect("range out of bounds")
} }
pub trait SerdeJsonEscapeIfTest {
fn char_needs_escape(&mut self, ch: char) -> serde_json::Result<bool>;
}
pub trait SerdeJsonEscapeIfTestResult {
fn to_result(self) -> serde_json::Result<bool>;
}
impl SerdeJsonEscapeIfTestResult for bool {
fn to_result(self) -> serde_json::Result<bool> {
Ok(self)
}
}
impl<E: Into<serde_json::Error>> SerdeJsonEscapeIfTestResult for Result<bool, E> {
fn to_result(self) -> serde_json::Result<bool> {
self.map_err(Into::into)
}
}
impl<T: ?Sized + FnMut(char) -> R, R: SerdeJsonEscapeIfTestResult> SerdeJsonEscapeIfTest for T {
fn char_needs_escape(&mut self, ch: char) -> serde_json::Result<bool> {
self(ch).to_result()
}
}
pub trait SerdeJsonEscapeIfFormatter: serde_json::ser::Formatter {
fn write_unicode_escape<W>(&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<Test, Base = serde_json::ser::CompactFormatter> {
pub base: Base,
pub test: Test,
}
impl<Test: SerdeJsonEscapeIfTest, Base: SerdeJsonEscapeIfFormatter> serde_json::ser::Formatter
for SerdeJsonEscapeIf<Test, Base>
{
fn write_null<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_null(writer)
}
fn write_bool<W>(&mut self, writer: &mut W, value: bool) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_bool(writer, value)
}
fn write_i8<W>(&mut self, writer: &mut W, value: i8) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_i8(writer, value)
}
fn write_i16<W>(&mut self, writer: &mut W, value: i16) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_i16(writer, value)
}
fn write_i32<W>(&mut self, writer: &mut W, value: i32) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_i32(writer, value)
}
fn write_i64<W>(&mut self, writer: &mut W, value: i64) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_i64(writer, value)
}
fn write_i128<W>(&mut self, writer: &mut W, value: i128) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_i128(writer, value)
}
fn write_u8<W>(&mut self, writer: &mut W, value: u8) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_u8(writer, value)
}
fn write_u16<W>(&mut self, writer: &mut W, value: u16) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_u16(writer, value)
}
fn write_u32<W>(&mut self, writer: &mut W, value: u32) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_u32(writer, value)
}
fn write_u64<W>(&mut self, writer: &mut W, value: u64) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_u64(writer, value)
}
fn write_u128<W>(&mut self, writer: &mut W, value: u128) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_u128(writer, value)
}
fn write_f32<W>(&mut self, writer: &mut W, value: f32) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_f32(writer, value)
}
fn write_f64<W>(&mut self, writer: &mut W, value: f64) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_f64(writer, value)
}
fn write_number_str<W>(&mut self, writer: &mut W, value: &str) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_number_str(writer, value)
}
fn begin_string<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.begin_string(writer)
}
fn end_string<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.end_string(writer)
}
fn write_string_fragment<W>(&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<W>(
&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<W>(&mut self, writer: &mut W, value: &[u8]) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.write_byte_array(writer, value)
}
fn begin_array<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.begin_array(writer)
}
fn end_array<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.end_array(writer)
}
fn begin_array_value<W>(&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<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.end_array_value(writer)
}
fn begin_object<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.begin_object(writer)
}
fn end_object<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.end_object(writer)
}
fn begin_object_key<W>(&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<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.end_object_key(writer)
}
fn begin_object_value<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.begin_object_value(writer)
}
fn end_object_value<W>(&mut self, writer: &mut W) -> io::Result<()>
where
W: ?Sized + io::Write,
{
self.base.end_object_value(writer)
}
fn write_raw_fragment<W>(&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<F: SerdeJsonEscapeIfFormatter, S: serde::Serialize + ?Sized>(
v: &S,
base: F,
) -> serde_json::Result<String> {
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<T: serde::Serialize + ?Sized>(v: &T) -> serde_json::Result<String> {
serialize_to_json_ascii_helper(v, serde_json::ser::CompactFormatter)
}
pub fn serialize_to_json_ascii_pretty<T: serde::Serialize + ?Sized>(
v: &T,
) -> serde_json::Result<String> {
serialize_to_json_ascii_helper(v, serde_json::ser::PrettyFormatter::new())
}
pub fn serialize_to_json_ascii_pretty_with_indent<T: serde::Serialize + ?Sized>(
v: &T,
indent: &str,
) -> serde_json::Result<String> {
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<str>) -> 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<str>) -> 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<str>);
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<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::borrow::Borrow<str> for InternedStrCompareAsStr {
fn borrow(&self) -> &str {
&self.0
}
}
pub(crate) fn copy_le_bytes_to_bitslice(
dest: &mut BitSlice<usize, Lsb0>,
bytes: &[u8],
msb_fill: bool,
) {
let (chunks, remainder) = bytes.as_chunks();
let mut filled_to = 0;
for (i, chunk) in chunks.iter().enumerate() {
if let Some(start_bit_index) = i.checked_mul(usize::BITS as usize)
&& start_bit_index < dest.len()
{
let end_bit_index = start_bit_index
.saturating_add(usize::BITS as usize)
.min(dest.len());
let bit_len = end_bit_index - start_bit_index;
let chunk = usize::from_le_bytes(*chunk);
dest[start_bit_index..end_bit_index].copy_from_bitslice(&chunk.view_bits()[..bit_len]);
filled_to = end_bit_index;
} else {
break;
}
}
if !remainder.is_empty() {
if let Some(start_bit_index) = chunks.len().checked_mul(usize::BITS as usize)
&& start_bit_index < dest.len()
{
let end_bit_index = start_bit_index
.saturating_add(usize::BITS as usize)
.min(dest.len());
let bit_len = end_bit_index - start_bit_index;
let mut chunk = [if msb_fill { !0 } else { 0 }; _];
chunk[..remainder.len()].copy_from_slice(remainder);
let chunk = usize::from_le_bytes(chunk);
dest[start_bit_index..end_bit_index].copy_from_bitslice(&chunk.view_bits()[..bit_len]);
filled_to = end_bit_index;
}
}
dest[filled_to..].fill(msb_fill);
}

View file

@ -25,7 +25,7 @@ impl<T: Type> ReadyValid<T> {
#[hdl] #[hdl]
pub fn firing_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> { pub fn firing_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> {
let expr = expr.to_expr(); let expr = expr.to_expr();
let option_ty = expr.ty().data; let option_ty = Expr::ty(expr).data;
#[hdl] #[hdl]
let firing_data = wire(option_ty); let firing_data = wire(option_ty);
connect(firing_data, option_ty.HdlNone()); connect(firing_data, option_ty.HdlNone());
@ -42,7 +42,7 @@ impl<T: Type> ReadyValid<T> {
) -> Expr<ReadyValid<R>> { ) -> Expr<ReadyValid<R>> {
let data = HdlOption::map(expr.data, f); let data = HdlOption::map(expr.data, f);
#[hdl] #[hdl]
let mapped = wire(ReadyValid[data.ty().HdlSome]); let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]);
connect(mapped.data, data); connect(mapped.data, data);
connect(expr.ready, mapped.ready); connect(expr.ready, mapped.ready);
mapped mapped
@ -81,7 +81,7 @@ pub fn queue<T: Type>(
let count: UInt = m.output(count_ty); let count: UInt = m.output(count_ty);
#[hdl] #[hdl]
let inp_index_reg: UInt = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
#[hdl] #[hdl]
let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
#[hdl] #[hdl]
@ -212,7 +212,9 @@ pub fn queue<T: Type>(
mod tests { mod tests {
use super::*; use super::*;
use crate::{ use crate::{
firrtl::ExportOptions, module::transform::simplify_enums::SimplifyEnumsKind, ty::StaticType, cli::FormalMode, firrtl::ExportOptions,
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
ty::StaticType,
}; };
use std::num::NonZero; use std::num::NonZero;
@ -241,13 +243,15 @@ mod tests {
/// happens to be in phase with the offending input or output). /// happens to be in phase with the offending input or output).
#[hdl_module] #[hdl_module]
fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
#[hdl]
let clk: Clock = m.input();
#[hdl] #[hdl]
let cd = wire(); let cd = wire();
connect( connect(
cd, cd,
#[hdl] #[hdl]
ClockDomain { ClockDomain {
clk: formal_global_clock(), clk,
rst: formal_reset().to_reset(), rst: formal_reset().to_reset(),
}, },
); );
@ -278,7 +282,7 @@ mod tests {
#[hdl] #[hdl]
let index_to_check = wire(index_ty); let index_to_check = wire(index_ty);
connect(index_to_check, any_const(index_ty)); connect(index_to_check, any_const(index_ty));
hdl_assume(cd.clk, index_to_check.cmp_lt(capacity.get()), ""); hdl_assume(clk, index_to_check.cmp_lt(capacity.get()), "");
// instantiate and connect the queue // instantiate and connect the queue
#[hdl] #[hdl]
@ -298,13 +302,13 @@ mod tests {
let expected_count_reg = reg_builder().clock_domain(cd).reset(count_ty.zero()); let expected_count_reg = reg_builder().clock_domain(cd).reset(count_ty.zero());
#[hdl] #[hdl]
if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) { if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) {
hdl_assert(cd.clk, expected_count_reg.cmp_ne(capacity.get()), ""); hdl_assert(clk, expected_count_reg.cmp_ne(capacity.get()), "");
connect_any(expected_count_reg, expected_count_reg + 1u8); connect_any(expected_count_reg, expected_count_reg + 1u8);
} else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) { } else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) {
hdl_assert(cd.clk, expected_count_reg.cmp_ne(count_ty.zero()), ""); hdl_assert(clk, expected_count_reg.cmp_ne(count_ty.zero()), "");
connect_any(expected_count_reg, expected_count_reg - 1u8); connect_any(expected_count_reg, expected_count_reg - 1u8);
} }
hdl_assert(cd.clk, expected_count_reg.cmp_eq(dut.count), ""); hdl_assert(clk, expected_count_reg.cmp_eq(dut.count), "");
// keep an independent write index into the FIFO's circular buffer // keep an independent write index into the FIFO's circular buffer
#[hdl] #[hdl]
@ -372,7 +376,7 @@ mod tests {
match inp_firing_data { match inp_firing_data {
// ... and we are not receiving data, then we must not // ... and we are not receiving data, then we must not
// transmit any data. // transmit any data.
HdlNone => hdl_assert(cd.clk, HdlOption::is_none(out_firing_data), ""), HdlNone => hdl_assert(clk, HdlOption::is_none(out_firing_data), ""),
// If we are indeed receiving some data... // If we are indeed receiving some data...
HdlSome(data_in) => { HdlSome(data_in) => {
#[hdl] #[hdl]
@ -380,9 +384,7 @@ mod tests {
// ... and transmitting at the same time, we // ... and transmitting at the same time, we
// must be transmitting the input data itself, // must be transmitting the input data itself,
// since the holding register is empty. // since the holding register is empty.
HdlSome(data_out) => { HdlSome(data_out) => hdl_assert(clk, data_out.cmp_eq(data_in), ""),
hdl_assert(cd.clk, data_out.cmp_eq(data_in), "")
}
// If we are receiving, but not transmitting, // If we are receiving, but not transmitting,
// store the received data in the holding // store the received data in the holding
// register. // register.
@ -397,11 +399,11 @@ mod tests {
match out_firing_data { match out_firing_data {
// ... and we are not transmitting it, we cannot // ... and we are not transmitting it, we cannot
// receive any more data. // receive any more data.
HdlNone => hdl_assert(cd.clk, HdlOption::is_none(inp_firing_data), ""), HdlNone => hdl_assert(clk, HdlOption::is_none(inp_firing_data), ""),
// If we are transmitting a previously stored value... // If we are transmitting a previously stored value...
HdlSome(data_out) => { HdlSome(data_out) => {
// ... it must be the same data we stored earlier. // ... it must be the same data we stored earlier.
hdl_assert(cd.clk, data_out.cmp_eq(stored), ""); hdl_assert(clk, data_out.cmp_eq(stored), "");
// Also, accept new data, if any. Otherwise, // Also, accept new data, if any. Otherwise,
// let the holding register become empty. // let the holding register become empty.
connect(stored_reg, inp_firing_data); connect(stored_reg, inp_firing_data);
@ -417,17 +419,17 @@ mod tests {
connect(dut.dbg.index_to_check, index_to_check); connect(dut.dbg.index_to_check, index_to_check);
#[hdl] #[hdl]
if let HdlSome(stored) = stored_reg { if let HdlSome(stored) = stored_reg {
hdl_assert(cd.clk, stored.cmp_eq(dut.dbg.stored), ""); hdl_assert(clk, stored.cmp_eq(dut.dbg.stored), "");
} }
// sync the read and write indices // sync the read and write indices
hdl_assert(cd.clk, inp_index_reg.cmp_eq(dut.dbg.inp_index), ""); hdl_assert(clk, inp_index_reg.cmp_eq(dut.dbg.inp_index), "");
hdl_assert(cd.clk, out_index_reg.cmp_eq(dut.dbg.out_index), ""); hdl_assert(clk, out_index_reg.cmp_eq(dut.dbg.out_index), "");
// the indices should never go past the capacity, but induction // the indices should never go past the capacity, but induction
// doesn't know that... // doesn't know that...
hdl_assert(cd.clk, inp_index_reg.cmp_lt(capacity.get()), ""); hdl_assert(clk, inp_index_reg.cmp_lt(capacity.get()), "");
hdl_assert(cd.clk, out_index_reg.cmp_lt(capacity.get()), ""); hdl_assert(clk, out_index_reg.cmp_lt(capacity.get()), "");
// strongly constrain the state of the holding register // strongly constrain the state of the holding register
// //
@ -455,7 +457,7 @@ mod tests {
connect(expected_stored, pending_reads.cmp_lt(dut.count)); connect(expected_stored, pending_reads.cmp_lt(dut.count));
// sync with the state of the holding register // sync with the state of the holding register
hdl_assert( hdl_assert(
cd.clk, clk,
expected_stored.cmp_eq(HdlOption::is_some(stored_reg)), expected_stored.cmp_eq(HdlOption::is_some(stored_reg)),
"", "",
); );

View file

@ -1,234 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::util::HashMap;
use hashbrown::hash_map::Entry;
use serde::{Deserialize, Serialize, de::Error};
use std::{
any::TypeId,
borrow::Cow,
fmt::Write,
hash::{BuildHasher, Hash, Hasher},
marker::PhantomData,
sync::Mutex,
};
pub(crate) struct SerdeByIdProperties<T: SerdeByIdTrait> {
type_id: TypeId,
type_name: &'static str,
_phantom: PhantomData<fn(T) -> T>,
}
impl<T: SerdeByIdTrait> Clone for SerdeByIdProperties<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: SerdeByIdTrait> Copy for SerdeByIdProperties<T> {}
impl<T: SerdeByIdTrait> SerdeByIdProperties<T> {
pub fn of<U: ?Sized + 'static>() -> Self {
Self {
type_id: TypeId::of::<U>(),
type_name: std::any::type_name::<U>(),
_phantom: PhantomData,
}
}
}
pub(crate) trait SerdeByIdTrait: Hash + Eq + Clone + 'static + Send {
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Self>;
fn static_table() -> &'static SerdeByIdTable<Self>;
const NAME: &'static str;
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[serde(transparent)]
struct SerdeRandomId([u32; 4]);
#[derive(Serialize, Deserialize)]
pub(crate) struct SerdeId<'a, T: SerdeByIdTrait> {
random_id: SerdeRandomId,
#[serde(borrow)]
type_name: Cow<'a, str>,
#[serde(skip)]
_phantom: PhantomData<fn(T) -> T>,
}
impl<'a, T: SerdeByIdTrait> Clone for SerdeId<'a, T> {
fn clone(&self) -> Self {
Self {
random_id: self.random_id,
type_name: self.type_name.clone(),
_phantom: PhantomData,
}
}
}
impl<'a, T: SerdeByIdTrait> Eq for SerdeId<'a, T> {}
impl<'a, 'b, T: SerdeByIdTrait> PartialEq<SerdeId<'b, T>> for SerdeId<'a, T> {
fn eq(&self, other: &SerdeId<'b, T>) -> bool {
let Self {
random_id,
type_name,
_phantom: _,
} = self;
*random_id == other.random_id && *type_name == other.type_name
}
}
impl<'a, T: SerdeByIdTrait> Hash for SerdeId<'a, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
let Self {
random_id,
type_name: _,
_phantom: _,
} = self;
random_id.hash(state);
}
}
struct SerdeByIdTableRest<T: SerdeByIdTrait> {
from_serde: HashMap<SerdeId<'static, T>, T>,
serde_id_random_state: std::hash::RandomState,
buffer: String,
}
impl<T: SerdeByIdTrait> Default for SerdeByIdTableRest<T> {
fn default() -> Self {
Self {
from_serde: Default::default(),
serde_id_random_state: Default::default(),
buffer: Default::default(),
}
}
}
impl<T: SerdeByIdTrait> SerdeByIdTableRest<T> {
fn add_new(&mut self, value: T) -> SerdeId<'static, T> {
let properties = value.serde_by_id_properties();
let mut try_number = 0u64;
let mut hasher = self.serde_id_random_state.build_hasher();
// extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits
write!(self.buffer, "{:?}", properties.type_id).expect("shouldn't ever fail");
self.buffer.hash(&mut hasher);
loop {
let mut hasher = hasher.clone();
try_number.hash(&mut hasher);
try_number += 1;
let key = SerdeId {
random_id: SerdeRandomId(std::array::from_fn(|i| {
let mut hasher = hasher.clone();
i.hash(&mut hasher);
hasher.finish() as u32
})),
type_name: Cow::Borrowed(properties.type_name),
_phantom: PhantomData,
};
match self.from_serde.entry(key) {
Entry::Occupied(_) => continue,
Entry::Vacant(e) => {
let key = e.key().clone();
e.insert(value);
return key;
}
}
}
}
}
pub(crate) struct SerdeByIdTableMut<T: SerdeByIdTrait> {
to_serde: HashMap<T, SerdeId<'static, T>>,
rest: SerdeByIdTableRest<T>,
}
impl<T: SerdeByIdTrait> Default for SerdeByIdTableMut<T> {
fn default() -> Self {
Self {
to_serde: Default::default(),
rest: Default::default(),
}
}
}
impl<T: SerdeByIdTrait> SerdeByIdTableMut<T> {
pub(crate) fn to_serde(&mut self, value: &T) -> SerdeId<'static, T> {
if let Some(retval) = self.to_serde.get(value) {
return retval.clone();
}
self.to_serde_insert(value)
}
#[cold]
fn to_serde_insert(&mut self, value: &T) -> SerdeId<'static, T> {
let value = value.clone();
let retval = self.rest.add_new(value.clone());
self.to_serde.insert(value, retval.clone());
retval
}
pub(crate) fn from_serde(&self, id: &SerdeId<'_, T>) -> Option<T> {
self.rest.from_serde.get(id).cloned()
}
}
pub(crate) struct SerdeByIdTable<T: SerdeByIdTrait>(Mutex<Option<SerdeByIdTableMut<T>>>);
impl<T: SerdeByIdTrait> SerdeByIdTable<T> {
pub(crate) const fn new() -> Self {
Self(Mutex::new(None))
}
pub(crate) fn to_serde(&self, value: &T) -> SerdeId<'static, T> {
self.0
.lock()
.expect("shouldn't be poison")
.get_or_insert_with(
#[cold]
|| Default::default(),
)
.to_serde(value)
}
pub(crate) fn from_serde(&self, id: &SerdeId<'_, T>) -> Option<T> {
self.0
.lock()
.expect("shouldn't be poison")
.get_or_insert_with(
#[cold]
|| Default::default(),
)
.from_serde(id)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, Ord, PartialOrd)]
pub(crate) struct SerdeById<T: SerdeByIdTrait> {
pub(crate) inner: T,
}
impl<'de, T: SerdeByIdTrait> Deserialize<'de> for SerdeById<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let id = SerdeId::deserialize(deserializer)?;
let inner = T::static_table().from_serde(&id).ok_or_else(|| {
D::Error::custom(format_args!(
"doesn't match any {} that was serialized this time this program was run: type_name={:?}",
T::NAME,
id.type_name,
))
})?;
Ok(Self { inner })
}
}
impl<T: SerdeByIdTrait> Serialize for SerdeById<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
T::static_table()
.to_serde(&self.inner)
.serialize(serializer)
}
}

View file

@ -1,352 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::util::{
HashMap,
map_trait::{self, Map, MapGet, OccupiedEntry as _, VacantEntry as _},
};
use petgraph::unionfind::UnionFind;
use std::{collections::BTreeMap, fmt, marker::PhantomData};
pub struct UnionFindMap<K, V, M = HashMap<K, usize>> {
uf: UnionFind<usize>,
keys_to_indexes: M,
values: Vec<Option<V>>,
_phantom: PhantomData<K>,
}
impl<K: fmt::Debug, V: fmt::Debug, M: Map<Key = K, Value = usize>> fmt::Debug
for UnionFindMap<K, V, M>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut indexes_to_keys = vec![None; self.len()];
for (k, &index) in self.keys_to_indexes.iter() {
indexes_to_keys[index] = Some(k);
}
let mut debug_map = f.debug_map();
for (index, key) in indexes_to_keys.into_iter().enumerate() {
if let Some(key) = key {
debug_map.key(key);
} else {
debug_map.key(&fmt::from_fn(|f| {
f.write_str("<<there's a misbehaving key>>")
}));
}
let set_index = self.uf.find(index);
debug_map.value(&fmt::from_fn(|f| {
write!(f, "@{set_index} ")?;
if set_index == index {
let Some(value) = &self.values[index] else {
unreachable!();
};
value.fmt(f)
} else {
Ok(())
}
}));
}
debug_map.finish()
}
}
impl<K, V, M: Map<Key = K, Value = usize>> UnionFindMap<K, V, M> {
/// returns the number of keys, not the number of sets/values
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn capacity(&self) -> usize {
self.values.capacity()
}
#[track_caller]
pub fn equiv<K1: ?Sized, K2: ?Sized>(&self, k1: &K1, k2: &K2) -> bool
where
M: MapGet<K1> + MapGet<K2>,
{
self.try_equiv(k1, k2).expect("key not found")
}
pub fn try_equiv<K1: ?Sized, K2: ?Sized>(&self, k1: &K1, k2: &K2) -> Option<bool>
where
M: MapGet<K1> + MapGet<K2>,
{
let &index1 = self.keys_to_indexes.get(k1)?;
let &index2 = self.keys_to_indexes.get(k2)?;
Some(self.uf.equiv(index1, index2))
}
#[track_caller]
pub fn find<Q: ?Sized>(&self, k: &Q) -> &V
where
M: MapGet<Q>,
{
self.try_find(k).expect("key not found")
}
pub fn try_find<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
M: MapGet<Q>,
{
let &index = self.keys_to_indexes.get(k)?;
self.values[self.uf.find(index)].as_ref()
}
#[track_caller]
pub fn find_mut<Q: ?Sized>(&mut self, k: &Q) -> &mut V
where
M: MapGet<Q>,
{
self.try_find_mut(k).expect("key not found")
}
pub fn try_find_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
where
M: MapGet<Q>,
{
let &index = self.keys_to_indexes.get(k)?;
self.values[self.uf.find_mut(index)].as_mut()
}
/// inserts a new key as a new set, otherwise replaces the value for the set containing the passed-in key
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
match self.entry(k) {
Entry::Vacant(entry) => {
entry.insert(v);
None
}
Entry::Occupied(mut entry) => Some(entry.insert(v)),
}
}
pub fn entry(&mut self, k: K) -> Entry<'_, K, V, M> {
match self.keys_to_indexes.entry(k) {
map_trait::Entry::Vacant(keys_to_indexes_entry) => Entry::Vacant(VacantEntry {
keys_to_indexes_entry,
uf: &mut self.uf,
values: &mut self.values,
}),
map_trait::Entry::Occupied(keys_to_indexes_entry) => {
let set_index = self.uf.find_mut(*keys_to_indexes_entry.get());
Entry::Occupied(OccupiedEntry {
keys_to_indexes_entry,
set_index,
values: &mut self.values,
})
}
}
}
/// Unify the two sets containing `k1` and `k2`.
/// If the sets were the same, returns `Some((false, value))`,
/// otherwise calling `merge` to merge their values and returning `Some((true, value))`.
/// Returns `None` if either of the keys weren't found.
pub fn try_union<K1: ?Sized, K2: ?Sized, F>(
&mut self,
k1: &K1,
k2: &K2,
merge: F,
) -> Option<(bool, &mut V)>
where
M: MapGet<K1> + MapGet<K2>,
F: FnOnce(&K1, V, &K2, V) -> V,
{
let &index1 = self.keys_to_indexes.get(k1)?;
let &index2 = self.keys_to_indexes.get(k2)?;
let index1 = self.uf.find_mut(index1);
let index2 = self.uf.find_mut(index2);
if index1 == index2 {
return Some((false, self.values[index1].as_mut()?));
}
assert!(self.uf.union(index1, index2));
let v1 = self.values[index1].take().expect("known to be Some");
let v2 = self.values[index2].take().expect("known to be Some");
let dest = &mut self.values[self.uf.find_mut(index1)];
let dest = dest.insert(merge(k1, v1, k2, v2));
Some((true, dest))
}
/// Unify the two sets containing `k1` and `k2`.
/// If the sets were the same, returns `(false, value)`,
/// otherwise calling `merge` to merge their values and returning `(true, value)`.
/// panics if either of the keys weren't found.
#[track_caller]
pub fn union<K1: ?Sized, K2: ?Sized, F>(&mut self, k1: &K1, k2: &K2, merge: F) -> (bool, &mut V)
where
M: MapGet<K1> + MapGet<K2>,
F: FnOnce(&K1, V, &K2, V) -> V,
{
self.try_union(k1, k2, merge).expect("key not found")
}
}
impl<K, V> UnionFindMap<K, V> {
pub fn new() -> Self {
Self::with_hasher(Default::default())
}
pub fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_and_hasher(capacity, Default::default())
}
}
impl<K, V> UnionFindMap<K, V, BTreeMap<K, usize>> {
pub const fn new_btree() -> Self {
Self {
uf: UnionFind::new_empty(),
keys_to_indexes: BTreeMap::new(),
values: Vec::new(),
_phantom: PhantomData,
}
}
}
impl<K, V, H> UnionFindMap<K, V, HashMap<K, usize, H>> {
pub const fn with_hasher(hash_builder: H) -> Self {
Self {
uf: UnionFind::new_empty(),
keys_to_indexes: HashMap::with_hasher(hash_builder),
values: Vec::new(),
_phantom: PhantomData,
}
}
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: H) -> Self {
Self {
uf: UnionFind::with_capacity(capacity),
keys_to_indexes: HashMap::with_capacity_and_hasher(capacity, hash_builder),
values: Vec::with_capacity(capacity),
_phantom: PhantomData,
}
}
}
impl<K, V, M: Default> Default for UnionFindMap<K, V, M> {
fn default() -> Self {
Self {
uf: UnionFind::new_empty(),
keys_to_indexes: M::default(),
values: Vec::new(),
_phantom: PhantomData,
}
}
}
pub struct OccupiedEntry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
keys_to_indexes_entry: M::OccupiedEntry<'a>,
set_index: usize,
values: &'a mut [Option<V>],
}
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> OccupiedEntry<'a, K, V, M> {
pub fn get(&self) -> &V {
let Some(v) = &self.values[self.set_index] else {
unreachable!()
};
v
}
pub fn get_mut(&mut self) -> &mut V {
let Some(v) = &mut self.values[self.set_index] else {
unreachable!()
};
v
}
/// replaces the value for this set
pub fn insert(&mut self, v: V) -> V {
std::mem::replace(self.get_mut(), v)
}
pub fn into_mut(self) -> &'a mut V {
let Some(v) = &mut self.values[self.set_index] else {
unreachable!()
};
v
}
pub fn key(&self) -> &K {
self.keys_to_indexes_entry.key()
}
}
pub struct VacantEntry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
keys_to_indexes_entry: M::VacantEntry<'a>,
uf: &'a mut UnionFind<usize>,
values: &'a mut Vec<Option<V>>,
}
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> VacantEntry<'a, K, V, M> {
/// inserts a new key as a new set
pub fn insert(self, v: V) -> &'a mut V {
self.insert_entry(v).into_mut()
}
/// inserts a new key as a new set
pub fn insert_entry(self, v: V) -> OccupiedEntry<'a, K, V, M> {
let Self {
keys_to_indexes_entry,
uf,
values,
} = self;
let set_index = uf.new_set();
values.push(Some(v));
OccupiedEntry {
keys_to_indexes_entry: keys_to_indexes_entry.insert_entry(set_index),
set_index,
values,
}
}
pub fn into_key(self) -> K {
self.keys_to_indexes_entry.into_key()
}
pub fn key(&self) -> &K {
self.keys_to_indexes_entry.key()
}
}
pub enum Entry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
Vacant(VacantEntry<'a, K, V, M>),
Occupied(OccupiedEntry<'a, K, V, M>),
}
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> Entry<'a, K, V, M> {
pub fn and_modify<F: FnOnce(&mut V)>(mut self, f: F) -> Self {
if let Self::Occupied(entry) = &mut self {
f(entry.get_mut());
}
self
}
/// inserts a new key as a new set, otherwise replaces the value for the set containing the passed-in key
pub fn insert_entry(self, v: V) -> OccupiedEntry<'a, K, V, M> {
match self {
Self::Vacant(entry) => entry.insert_entry(v),
Self::Occupied(mut entry) => {
entry.insert(v);
entry
}
}
}
pub fn key(&self) -> &K {
match self {
Self::Vacant(entry) => entry.key(),
Self::Occupied(entry) => entry.key(),
}
}
/// inserts a new key as a new set
pub fn or_default(self) -> &'a mut V
where
V: Default,
{
self.or_insert_with(V::default)
}
/// inserts a new key as a new set
pub fn or_insert(self, v: V) -> &'a mut V {
match self {
Self::Vacant(entry) => entry.insert(v),
Self::Occupied(entry) => entry.into_mut(),
}
}
/// inserts a new key as a new set
pub fn or_insert_with<F: FnOnce() -> V>(self, f: F) -> &'a mut V {
match self {
Self::Vacant(entry) => entry.insert(f()),
Self::Occupied(entry) => entry.into_mut(),
}
}
/// inserts a new key as a new set
pub fn or_insert_with_key<F: FnOnce(&K) -> V>(self, f: F) -> &'a mut V {
match self {
Self::Vacant(entry) => {
let v = f(entry.key());
entry.insert(v)
}
Self::Occupied(entry) => entry.into_mut(),
}
}
}

View file

@ -1,12 +0,0 @@
// 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<Item = crate::build::DynJobKind> {
xilinx::built_in_job_kinds()
}
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> {
xilinx::built_in_platforms()
}

View file

@ -1,207 +0,0 @@
// 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<str>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct XdcLocationAnnotation {
pub location: Interned<str>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct XdcCreateClockAnnotation {
/// clock period in nanoseconds
pub period: NotNan<f64>,
}
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<Device>,
}
impl XilinxArgs {
pub fn require_device(
&self,
platform: Option<&DynPlatform>,
global_params: &GlobalParams,
) -> clap::error::Result<Device> {
if let Some(device) = self.device {
return Ok(device);
}
if let Some(device) =
platform.and_then(|platform| platform.aspects().get_single_by_type::<Device>().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<E>(self, v: &str) -> Result<Self::Value, E>
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_string(DeviceVisitor)
}
}
impl Serialize for $Device {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<Item = crate::build::DynJobKind> {
arty_a7::built_in_job_kinds()
.into_iter()
.chain(yosys_nextpnr_prjxray::built_in_job_kinds())
}
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> {
arty_a7::built_in_platforms()
.into_iter()
.chain(yosys_nextpnr_prjxray::built_in_platforms())
}

View file

@ -1,404 +0,0 @@
// 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<PlatformAspectSet> = 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<ClockInput>; 4],
rst: Peripheral<Reset>,
rst_sync: Peripheral<SyncReset>,
ld0: Peripheral<RgbLed>,
ld1: Peripheral<RgbLed>,
ld2: Peripheral<RgbLed>,
ld3: Peripheral<RgbLed>,
ld4: Peripheral<Led>,
ld5: Peripheral<Led>,
ld6: Peripheral<Led>,
ld7: Peripheral<Led>,
uart: Peripheral<Uart>,
// TODO: add rest of peripherals when we need them
}
impl Peripherals for ArtyA7Peripherals {
fn append_peripherals<'a>(&'a self, peripherals: &mut Vec<PeripheralRef<'a, CanonicalType>>) {
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<str> {
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<Item = crate::build::DynJobKind> {
[]
}
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = DynPlatform> {
ArtyA7Platform::VARIANTS
.iter()
.map(|&v| DynPlatform::new(v))
}

View file

@ -1,50 +0,0 @@
// 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();
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr}, expr::{Expr, Flow, ToExpr},
intern::Interned, intern::Interned,
module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire}, module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire},
source_location::SourceLocation, source_location::SourceLocation,
@ -16,16 +16,7 @@ pub struct Wire<T: Type> {
ty: T, ty: T,
} }
impl<T: Type> ValueType for Wire<T> { impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
type Type = T;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
self.ty
}
}
impl<T: Type> fmt::Debug for Wire<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Wire({:?}: ", self.name)?; write!(f, "Wire({:?}: ", self.name)?;
self.ty.fmt(f)?; self.ty.fmt(f)?;
@ -58,13 +49,14 @@ impl<T: Type> Wire<T> {
ty: T::from_canonical(ty), ty: T::from_canonical(ty),
} }
} }
#[track_caller] pub fn ty(&self) -> T {
self.ty
}
pub fn new_unchecked( pub fn new_unchecked(
scoped_name: ScopedNameId, scoped_name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
ty: T, ty: T,
) -> Self { ) -> Self {
scoped_name.0.assert_is_name_id();
Self { Self {
name: scoped_name, name: scoped_name,
source_location, source_location,
@ -78,7 +70,7 @@ impl<T: Type> Wire<T> {
self.containing_module_name_id().0 self.containing_module_name_id().0
} }
pub fn containing_module_name_id(&self) -> NameId { pub fn containing_module_name_id(&self) -> NameId {
self.name.0.unwrap_name_id() self.name.0
} }
pub fn name(&self) -> Interned<str> { pub fn name(&self) -> Interned<str> {
self.name_id().0 self.name_id().0

File diff suppressed because it is too large Load diff

View file

@ -1,6 +0,0 @@
<!--
SPDX-License-Identifier: LGPL-3.0-or-later
See Notices.txt for copyright information
-->
`my_test.vcd` is used in the doctest of `fayalite::testing::checked_vcd_output`

View file

@ -1,13 +0,0 @@
$timescale 1 ps $end
$scope module my_module $end
$var wire 8 gAF7X a $end
$var wire 8 QS=a/ b $end
$upscope $end
$enddefinitions $end
$dumpvars
b0 gAF7X
b0 QS=a/
$end
#1000000
b1100100 gAF7X
b101010 QS=a/

View file

@ -2,7 +2,19 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
//! Formal tests in Fayalite //! Formal tests in Fayalite
use fayalite::prelude::*; 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,
};
/// Test hidden state /// Test hidden state
/// ///
@ -107,7 +119,7 @@ mod hidden_state {
FormalMode::Prove, FormalMode::Prove,
16, 16,
None, None,
Default::default(), ExportOptions::default(),
); );
// here a couple of cycles is enough // here a couple of cycles is enough
assert_formal( assert_formal(
@ -116,7 +128,7 @@ mod hidden_state {
FormalMode::Prove, FormalMode::Prove,
2, 2,
None, None,
Default::default(), ExportOptions::default(),
); );
} }
} }
@ -230,7 +242,7 @@ mod memory {
#[hdl] #[hdl]
let wr: WritePort<DynSize> = wire(WritePort[n]); let wr: WritePort<DynSize> = wire(WritePort[n]);
connect(wr.addr, any_seq(UInt[n])); connect(wr.addr, any_seq(UInt[n]));
connect(wr.data, any_seq(UInt::<8>::new_static())); connect(wr.data, any_seq(UInt::<8>::TYPE));
connect(wr.en, any_seq(Bool)); connect(wr.en, any_seq(Bool));
#[hdl] #[hdl]
let dut = instance(example_sram(n)); let dut = instance(example_sram(n));
@ -277,7 +289,7 @@ mod memory {
FormalMode::Prove, FormalMode::Prove,
2, 2,
None, None,
Default::default(), ExportOptions::default(),
); );
} }
} }

View file

@ -4,6 +4,7 @@ use fayalite::{
bundle::BundleType, bundle::BundleType,
enum_::EnumType, enum_::EnumType,
int::{BoolOrIntType, IntType}, int::{BoolOrIntType, IntType},
phantom_const::PhantomConst,
prelude::*, prelude::*,
ty::StaticType, ty::StaticType,
}; };
@ -196,61 +197,3 @@ 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!(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!(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 +>); 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<P: PhantomConstGet<MyPhantomConstInner>> = DynSize;
#[hdl(outline_generated, get(|v| v.b))]
pub type GetB<P: PhantomConstGet<MyPhantomConstInner>> = UInt;
#[hdl(outline_generated, no_static)]
pub struct MyTypeWithPhantomConstParameter<P: PhantomConstGet<MyPhantomConstInner>> {
pub a: ArrayType<Bool, GetA<P>>,
pub b: HdlOption<GetB<P>>,
}
#[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<T> {
v: T,
}
#[hdl(outline_generated)]
pub(crate) struct MyPubCrateTypeWithArg<T> {
v: T,
}
#[hdl(outline_generated)]
pub struct MyTypeWithPrivateMembersWithArg<T> {
a: MyPrivateTypeWithArg<T>,
pub(crate) b: MyPubCrateTypeWithArg<T>,
pub c: T,
}
#[hdl(outline_generated)]
pub enum EnumWithOnlyOneVariant {
A,
}
#[hdl(outline_generated)]
pub enum EnumWithOnlyOneVariant2<T> {
A(T),
}

View file

@ -1,166 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::{prelude::*, ty::SimValueDebug};
use std::fmt;
#[hdl(outline_generated)]
struct MyStruct0<T, S: Size> {
v: T,
a: ArrayType<UInt<8>, S>,
}
#[hdl]
#[test]
fn check_my_struct0() {
let ty = MyStruct0[UInt[8]][3];
assert_eq!(
format!("{ty:?}"),
"MyStruct0 { v: UInt<8>, a: Array<UInt<8>, 3> }",
);
assert_eq!(
format!("{:?}", ty.mask_type()),
"MaskType<MyStruct0> { v: Bool, a: Array<Bool, 3> }",
);
let v = #[hdl(sim)]
MyStruct0::<_, _> {
v: 0x23u8,
a: [1u8, 2, 3],
};
assert_eq!(
format!("{v:?}"),
"MyStruct0 { v: 0x23_u8, a: [0x1_u8, 0x2_u8, 0x3_u8] }",
);
}
#[hdl(outline_generated, custom_debug())]
struct MyStruct1<T, S: Size> {
v: T,
a: ArrayType<UInt<8>, S>,
}
impl<T: Type, S: Size> fmt::Debug for MyStruct1<T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { v, a } = self;
f.debug_struct("Custom<MyStruct1>")
.field("v", v)
.field("a", a)
.finish()
}
}
impl<T: Type, S: Size> SimValueDebug for MyStruct1<T, S> {
#[hdl]
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
#[hdl(sim)]
let Self { v, a } = value;
f.debug_struct("Custom<MyStruct1>")
.field("v", &v)
.field("a", &a)
.finish()
}
}
#[hdl]
#[test]
fn check_my_struct1() {
let ty = MyStruct1[UInt[8]][3];
assert_eq!(
format!("{ty:?}"),
"Custom<MyStruct1> { v: UInt<8>, a: Array<UInt<8>, 3> }",
);
assert_eq!(
format!("{:?}", ty.mask_type()),
"MaskType<MyStruct1> { v: Bool, a: Array<Bool, 3> }",
);
let v = #[hdl(sim)]
MyStruct1::<_, _> {
v: 0x23u8,
a: [1u8, 2, 3],
};
assert_eq!(
format!("{v:?}"),
"Custom<MyStruct1> { v: 0x23_u8, a: [0x1_u8, 0x2_u8, 0x3_u8] }",
);
}
#[hdl(outline_generated)]
enum MyEnum0<T, S: Size> {
Unit,
V(T),
A(ArrayType<UInt<8>, S>),
}
#[hdl]
#[test]
fn check_my_enum0() {
let ty = MyEnum0[UInt[8]][3];
assert_eq!(
format!("{ty:?}"),
"MyEnum0 { Unit: (), V: UInt<8>, A: Array<UInt<8>, 3> }",
);
let v = #[hdl(sim)]
ty.Unit();
assert_eq!(format!("{v:?}"), "Unit");
let v = #[hdl(sim)]
ty.V(0x23u8);
assert_eq!(format!("{v:?}"), "V(0x23_u8)");
let v = #[hdl(sim)]
ty.A([1u8, 2, 3]);
assert_eq!(format!("{v:?}"), "A([0x1_u8, 0x2_u8, 0x3_u8])");
}
#[hdl(outline_generated, custom_debug())]
enum MyEnum1<T, S: Size> {
Unit,
V(T),
A(ArrayType<UInt<8>, S>),
}
impl<T: Type, S: Size> fmt::Debug for MyEnum1<T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { Unit, V, A } = self;
f.debug_struct("Custom<MyEnum1>")
.field("Unit", Unit)
.field("V", V)
.field("A", A)
.finish()
}
}
impl<T: Type, S: Size> SimValueDebug for MyEnum1<T, S> {
#[hdl]
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
type SimValueT<T> = <T as Type>::SimValue;
match value {
SimValueT::<Self>::Unit(_) => f.write_str("MyEnum1::Unit"),
SimValueT::<Self>::V(v, _) => f.debug_tuple("MyEnum1::V").field(v).finish(),
SimValueT::<Self>::A(a, _) => f.debug_tuple("MyEnum1::A").field(a).finish(),
SimValueT::<Self>::Unknown(_) => f.write_str("MyEnum1::Unknown"),
}
}
}
#[hdl]
#[test]
fn check_my_enum1() {
let ty = MyEnum1[UInt[8]][3];
assert_eq!(
format!("{ty:?}"),
"Custom<MyEnum1> { Unit: (), V: UInt<8>, A: Array<UInt<8>, 3> }",
);
let v = #[hdl(sim)]
ty.Unit();
assert_eq!(format!("{v:?}"), "MyEnum1::Unit");
let v = #[hdl(sim)]
ty.V(0x23u8);
assert_eq!(format!("{v:?}"), "MyEnum1::V(0x23_u8)");
let v = #[hdl(sim)]
ty.A([1u8, 2, 3]);
assert_eq!(format!("{v:?}"), "MyEnum1::A([0x1_u8, 0x2_u8, 0x3_u8])");
}

View file

@ -6,14 +6,13 @@ use fayalite::{
int::{UIntInRange, UIntInRangeInclusive}, int::{UIntInRange, UIntInRangeInclusive},
intern::Intern, intern::Intern,
module::transform::simplify_enums::SimplifyEnumsKind, module::transform::simplify_enums::SimplifyEnumsKind,
platform::PlatformIOBuilder,
prelude::*, prelude::*,
reset::ResetType, reset::ResetType,
ty::StaticType, ty::StaticType,
}; };
use serde_json::json; use serde_json::json;
#[hdl(outline_generated, cmp_eq)] #[hdl(outline_generated)]
pub enum TestEnum { pub enum TestEnum {
A, A,
B(UInt<8>), B(UInt<8>),
@ -215,7 +214,7 @@ where
let o: Array<T, N> = m.output(); let o: Array<T, N> = m.output();
let bytes = v.to_string().as_bytes().to_expr(); let bytes = v.to_string().as_bytes().to_expr();
#[hdl] #[hdl]
let o2: Array<UInt<8>> = m.output(bytes.ty()); let o2: Array<UInt<8>> = m.output(Expr::ty(bytes));
connect( connect(
o, o,
#[hdl] #[hdl]
@ -679,224 +678,6 @@ circuit check_enum_literals:
}; };
} }
#[hdl_module(outline_generated)]
pub fn check_enum_cmp_eq() {
#[hdl]
let lhs: TestEnum = m.input();
#[hdl]
let rhs: TestEnum = m.input();
#[hdl]
let eq: Bool = m.output();
connect(eq, lhs.cmp_eq(rhs));
}
#[test]
fn test_enum_cmp_eq() {
let _n = SourceLocation::normalize_files_for_tests();
let m = check_enum_cmp_eq();
dbg!(m);
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_enums: None,
..ExportOptions::default()
},
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_enum_cmp_eq:
type Ty0 = {|A, B: UInt<8>, C: UInt<1>[3]|}
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
input rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
wire _cast_enum_to_bits_expr: UInt<10>
match lhs:
A:
connect _cast_enum_to_bits_expr, UInt<10>(0)
B(_cast_enum_to_bits_expr_B):
connect _cast_enum_to_bits_expr, pad(cat(_cast_enum_to_bits_expr_B, UInt<2>(1)), 10)
C(_cast_enum_to_bits_expr_C):
wire _cast_array_to_bits_expr: UInt<1>[3]
connect _cast_array_to_bits_expr[0], _cast_enum_to_bits_expr_C[0]
connect _cast_array_to_bits_expr[1], _cast_enum_to_bits_expr_C[1]
connect _cast_array_to_bits_expr[2], _cast_enum_to_bits_expr_C[2]
wire _cast_to_bits_expr: UInt<3>
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
connect _cast_enum_to_bits_expr, pad(cat(_cast_to_bits_expr, UInt<2>(2)), 10)
wire _cast_enum_to_bits_expr_1: UInt<10>
match rhs:
A:
connect _cast_enum_to_bits_expr_1, UInt<10>(0)
B(_cast_enum_to_bits_expr_B_1):
connect _cast_enum_to_bits_expr_1, pad(cat(_cast_enum_to_bits_expr_B_1, UInt<2>(1)), 10)
C(_cast_enum_to_bits_expr_C_1):
wire _cast_array_to_bits_expr_1: UInt<1>[3]
connect _cast_array_to_bits_expr_1[0], _cast_enum_to_bits_expr_C_1[0]
connect _cast_array_to_bits_expr_1[1], _cast_enum_to_bits_expr_C_1[1]
connect _cast_array_to_bits_expr_1[2], _cast_enum_to_bits_expr_C_1[2]
wire _cast_to_bits_expr_1: UInt<3>
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
connect _cast_enum_to_bits_expr_1, pad(cat(_cast_to_bits_expr_1, UInt<2>(2)), 10)
connect eq, eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 5:1]
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody),
..ExportOptions::default()
},
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_enum_cmp_eq:
type Ty0 = {|A, B, C|}
type Ty1 = {tag: Ty0, body: UInt<8>}
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input lhs: Ty1 @[module-XXXXXXXXXX.rs 2:1]
input rhs: Ty1 @[module-XXXXXXXXXX.rs 3:1]
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1]
connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1]
connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1]
wire _cast_enum_to_bits_expr: UInt<2>
match lhs.tag:
A:
connect _cast_enum_to_bits_expr, UInt<2>(0)
B:
connect _cast_enum_to_bits_expr, UInt<2>(1)
C:
connect _cast_enum_to_bits_expr, UInt<2>(2)
wire _cast_enum_to_bits_expr_1: UInt<2>
match rhs.tag:
A:
connect _cast_enum_to_bits_expr_1, UInt<2>(0)
B:
connect _cast_enum_to_bits_expr_1, UInt<2>(1)
C:
connect _cast_enum_to_bits_expr_1, UInt<2>(2)
when eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1): @[module-XXXXXXXXXX.rs 1:1]
match lhs.tag: @[module-XXXXXXXXXX.rs 1:1]
A:
connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1]
B:
connect __enum_structural_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 1:1]
C:
wire _cast_bits_to_array_expr: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
wire _cast_bits_to_array_expr_1: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
wire _array_structural_eq: UInt<1>
connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]))
wire _array_structural_eq_1: UInt<1>
connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2]))
connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1]
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
..ExportOptions::default()
},
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_enum_cmp_eq:
type Ty0 = {tag: UInt<2>, body: UInt<8>}
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
input rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1]
connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1]
connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1]
when eq(lhs.tag, rhs.tag): @[module-XXXXXXXXXX.rs 1:1]
when eq(lhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 1:1]
connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1]
else when eq(lhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 1:1]
connect __enum_structural_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 1:1]
else:
wire _cast_bits_to_array_expr: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
wire _cast_bits_to_array_expr_1: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
wire _array_structural_eq: UInt<1>
connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]))
wire _array_structural_eq_1: UInt<1>
connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2]))
connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1]
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithUInt),
..ExportOptions::default()
},
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_enum_cmp_eq:
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input lhs: UInt<10> @[module-XXXXXXXXXX.rs 2:1]
input rhs: UInt<10> @[module-XXXXXXXXXX.rs 3:1]
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1]
connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1]
connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1]
when eq(bits(lhs, 1, 0), bits(rhs, 1, 0)): @[module-XXXXXXXXXX.rs 1:1]
when eq(bits(lhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 1:1]
connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1]
else when eq(bits(lhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 1:1]
connect __enum_structural_eq, eq(bits(bits(lhs, 9, 2), 7, 0), bits(bits(rhs, 9, 2), 7, 0)) @[module-XXXXXXXXXX.rs 1:1]
else:
wire _cast_bits_to_array_expr: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened[0], bits(bits(bits(lhs, 9, 2), 2, 0), 0, 0)
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
connect _cast_bits_to_array_expr_flattened[1], bits(bits(bits(lhs, 9, 2), 2, 0), 1, 1)
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
connect _cast_bits_to_array_expr_flattened[2], bits(bits(bits(lhs, 9, 2), 2, 0), 2, 2)
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
wire _cast_bits_to_array_expr_1: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(bits(rhs, 9, 2), 2, 0), 0, 0)
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(bits(rhs, 9, 2), 2, 0), 1, 1)
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(bits(rhs, 9, 2), 2, 0), 2, 2)
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
wire _array_structural_eq: UInt<1>
connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]))
wire _array_structural_eq_1: UInt<1>
connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2]))
connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1]
",
};
}
#[hdl_module(outline_generated)] #[hdl_module(outline_generated)]
pub fn check_struct_enum_match() { pub fn check_struct_enum_match() {
#[hdl] #[hdl]
@ -1394,7 +1175,7 @@ pub fn check_memory_init() {
let waddr2: UInt<4> = m.input(); let waddr2: UInt<4> = m.input();
#[hdl] #[hdl]
let wdata2: UInt<31> = m.input(); let wdata2: UInt<31> = m.input();
let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static::<UInt<31>>())); let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static()));
#[hdl] #[hdl]
let mut mem2 = memory_with_init(mem_init2); let mut mem2 = memory_with_init(mem_init2);
let read_port2 = mem2.new_read_port(); let read_port2 = mem2.new_read_port();
@ -2002,7 +1783,7 @@ pub fn check_memory_of_bundle_of_arrays() {
[(i * i).wrapping_mul(2), (i * i).wrapping_mul(3)], [(i * i).wrapping_mul(2), (i * i).wrapping_mul(3)],
[(i * i).wrapping_mul(i), i * 2], [(i * i).wrapping_mul(i), i * 2],
], ],
i.cast_to_static::<UInt<2>>(), i.cast_to_static(),
) )
})); }));
#[hdl] #[hdl]
@ -2172,9 +1953,9 @@ pub fn check_memory_of_array_of_bundle() {
let clk: Clock = m.input(); let clk: Clock = m.input();
let mem_init = Vec::from_iter((0..0x10u8).map(|i| { let mem_init = Vec::from_iter((0..0x10u8).map(|i| {
[ [
(i, i.cast_to_static::<SInt<1>>()), (i, i.cast_to_static()),
((i * i), (i / 2).cast_to_static::<SInt<1>>()), ((i * i), (i / 2).cast_to_static()),
((i * i).wrapping_mul(i), (i / 4).cast_to_static::<SInt<1>>()), ((i * i).wrapping_mul(i), (i / 4).cast_to_static()),
] ]
})); }));
#[hdl] #[hdl]
@ -2487,8 +2268,7 @@ pub fn check_memory_of_bundle() {
let wmask: (Bool, Bool) = m.input(); let wmask: (Bool, Bool) = m.input();
#[hdl] #[hdl]
let clk: Clock = m.input(); let clk: Clock = m.input();
let mem_init = let mem_init = Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static())));
Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static::<SInt<1>>())));
#[hdl] #[hdl]
let mut mem = memory_with_init(mem_init); let mut mem = memory_with_init(mem_init);
let read_port = mem.new_read_port(); let read_port = mem.new_read_port();
@ -3637,176 +3417,20 @@ circuit check_formal: %[[
input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1] input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1] input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1]
input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1] input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1]
inst formal_reset of formal_reset @[builtin 1:1] inst formal_reset of formal_reset @[formal.rs 185:24]
assert(clk, pred1, and(en1, not(formal_reset.rst)), "en check 1") @[module-XXXXXXXXXX.rs 9:1] assert(clk, pred1, and(en1, not(formal_reset.rst)), "en check 1") @[module-XXXXXXXXXX.rs 9:1]
assume(clk, pred2, and(en2, not(formal_reset.rst)), "en check 2") @[module-XXXXXXXXXX.rs 10:1] inst formal_reset_1 of formal_reset @[formal.rs 185:24]
cover(clk, pred3, and(en3, not(formal_reset.rst)), "en check 3") @[module-XXXXXXXXXX.rs 11:1] assume(clk, pred2, and(en2, not(formal_reset_1.rst)), "en check 2") @[module-XXXXXXXXXX.rs 10:1]
assert(clk, pred1, and(UInt<1>(0h1), not(formal_reset.rst)), "check 1") @[module-XXXXXXXXXX.rs 12:1] inst formal_reset_2 of formal_reset @[formal.rs 185:24]
assume(clk, pred2, and(UInt<1>(0h1), not(formal_reset.rst)), "check 2") @[module-XXXXXXXXXX.rs 13:1] cover(clk, pred3, and(en3, not(formal_reset_2.rst)), "en check 3") @[module-XXXXXXXXXX.rs 11:1]
cover(clk, pred3, and(UInt<1>(0h1), not(formal_reset.rst)), "check 3") @[module-XXXXXXXXXX.rs 14:1] inst formal_reset_3 of formal_reset @[formal.rs 185:24]
extmodule formal_reset: @[builtin 1:1] assert(clk, pred1, and(UInt<1>(0h1), not(formal_reset_3.rst)), "check 1") @[module-XXXXXXXXXX.rs 12:1]
output rst: UInt<1> @[builtin 1:1] inst formal_reset_4 of formal_reset @[formal.rs 185:24]
defname = __fayalite_formal_reset assume(clk, pred2, and(UInt<1>(0h1), not(formal_reset_4.rst)), "check 2") @[module-XXXXXXXXXX.rs 13:1]
"#, inst formal_reset_5 of formal_reset @[formal.rs 185:24]
}; cover(clk, pred3, and(UInt<1>(0h1), not(formal_reset_5.rst)), "check 3") @[module-XXXXXXXXXX.rs 14:1]
} extmodule formal_reset: @[formal.rs 169:5]
output rst: UInt<1> @[formal.rs 172:32]
#[hdl_module(outline_generated)]
pub fn check_formal_input() {
#[hdl]
let bool_in: Bool = m.input();
#[hdl]
let bool_out: Bool = m.output();
#[hdl]
let any_const_out1: Bool = m.output();
#[hdl]
let any_const_out2: UInt<16> = m.output();
#[hdl]
let any_const_out3: SInt<12> = m.output();
#[hdl]
let any_seq_out: UInt<10> = m.output();
#[hdl]
let all_const_out: UInt<10> = m.output();
#[hdl]
let all_seq_out: UInt<10> = m.output();
#[hdl]
let bool_reg = reg_builder()
.clock_domain(
#[hdl]
ClockDomain {
clk: formal_global_clock(),
rst: formal_reset(),
},
)
.reset(false);
connect(bool_reg, bool_in);
connect(bool_out, bool_reg);
connect(any_const_out1, any_const(StaticType::TYPE));
connect(any_const_out2, any_const(StaticType::TYPE));
connect(any_const_out3, any_const(StaticType::TYPE));
connect(any_seq_out, any_seq(StaticType::TYPE));
connect(all_const_out, all_const(StaticType::TYPE));
connect(all_seq_out, all_seq(StaticType::TYPE));
}
#[test]
fn test_formal_input() {
let _n = SourceLocation::normalize_files_for_tests();
let m = check_formal_input();
dbg!(m);
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
"/test/check_formal_input.fir": r#"FIRRTL version 3.2.0
circuit check_formal_input: %[[
{
"class": "firrtl.AttributeAnnotation",
"description": "gclk",
"target": "~check_formal_input|check_formal_input>formal_global_clock"
},
{
"class": "firrtl.transforms.DontTouchAnnotation",
"target": "~check_formal_input|check_formal_input>formal_global_clock"
},
{
"class": "firrtl.AttributeAnnotation",
"description": "anyconst",
"target": "~check_formal_input|check_formal_input>any_const"
},
{
"class": "firrtl.transforms.DontTouchAnnotation",
"target": "~check_formal_input|check_formal_input>any_const"
},
{
"class": "firrtl.AttributeAnnotation",
"description": "anyconst",
"target": "~check_formal_input|check_formal_input>any_const_1"
},
{
"class": "firrtl.transforms.DontTouchAnnotation",
"target": "~check_formal_input|check_formal_input>any_const_1"
},
{
"class": "firrtl.AttributeAnnotation",
"description": "anyconst",
"target": "~check_formal_input|check_formal_input>any_const_2"
},
{
"class": "firrtl.transforms.DontTouchAnnotation",
"target": "~check_formal_input|check_formal_input>any_const_2"
},
{
"class": "firrtl.AttributeAnnotation",
"description": "anyseq",
"target": "~check_formal_input|check_formal_input>any_seq"
},
{
"class": "firrtl.transforms.DontTouchAnnotation",
"target": "~check_formal_input|check_formal_input>any_seq"
},
{
"class": "firrtl.AttributeAnnotation",
"description": "allconst",
"target": "~check_formal_input|check_formal_input>all_const"
},
{
"class": "firrtl.transforms.DontTouchAnnotation",
"target": "~check_formal_input|check_formal_input>all_const"
},
{
"class": "firrtl.AttributeAnnotation",
"description": "allseq",
"target": "~check_formal_input|check_formal_input>all_seq"
},
{
"class": "firrtl.transforms.DontTouchAnnotation",
"target": "~check_formal_input|check_formal_input>all_seq"
},
{
"class": "firrtl.transforms.BlackBoxInlineAnno",
"name": "fayalite_formal_reset.v",
"text": "module __fayalite_formal_reset(output rst);\n assign rst = $initstate;\nendmodule\n",
"target": "~check_formal_input|formal_reset"
}
]]
type Ty0 = {clk: Clock, rst: UInt<1>}
type Ty1 = {rst: UInt<1>}
module check_formal_input: @[module-XXXXXXXXXX.rs 1:1]
input bool_in: UInt<1> @[module-XXXXXXXXXX.rs 2:1]
output bool_out: UInt<1> @[module-XXXXXXXXXX.rs 3:1]
output any_const_out1: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
output any_const_out2: UInt<16> @[module-XXXXXXXXXX.rs 5:1]
output any_const_out3: SInt<12> @[module-XXXXXXXXXX.rs 6:1]
output any_seq_out: UInt<10> @[module-XXXXXXXXXX.rs 7:1]
output all_const_out: UInt<10> @[module-XXXXXXXXXX.rs 8:1]
output all_seq_out: UInt<10> @[module-XXXXXXXXXX.rs 9:1]
wire _bundle_literal_expr_1: Ty0
connect _bundle_literal_expr_1.clk, asClock(UInt<1>(0h0))
connect _bundle_literal_expr_1.rst, UInt<1>(0h0)
reg formal_global_clock: UInt<1>, _bundle_literal_expr_1.clk @[builtin 1:1]
inst formal_reset of formal_reset @[builtin 1:1]
reg any_const: UInt<1>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 13:1]
reg any_const_1: UInt<16>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 15:1]
reg any_const_2: SInt<12>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 17:1]
reg any_seq: UInt<10>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 19:1]
reg all_const: UInt<10>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 21:1]
reg all_seq: UInt<10>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 23:1]
wire _bundle_literal_expr: Ty0
connect _bundle_literal_expr.clk, asClock(formal_global_clock)
connect _bundle_literal_expr.rst, formal_reset.rst
regreset bool_reg: UInt<1>, _bundle_literal_expr.clk, _bundle_literal_expr.rst, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 10:1]
connect bool_reg, bool_in @[module-XXXXXXXXXX.rs 11:1]
connect bool_out, bool_reg @[module-XXXXXXXXXX.rs 12:1]
connect any_const_out1, any_const @[module-XXXXXXXXXX.rs 14:1]
connect any_const_out2, any_const_1 @[module-XXXXXXXXXX.rs 16:1]
connect any_const_out3, any_const_2 @[module-XXXXXXXXXX.rs 18:1]
connect any_seq_out, any_seq @[module-XXXXXXXXXX.rs 20:1]
connect all_const_out, all_const @[module-XXXXXXXXXX.rs 22:1]
connect all_seq_out, all_seq @[module-XXXXXXXXXX.rs 24:1]
extmodule formal_reset: @[builtin 1:1]
output rst: UInt<1> @[builtin 1:1]
defname = __fayalite_formal_reset defname = __fayalite_formal_reset
"#, "#,
}; };
@ -3939,10 +3563,21 @@ circuit check_enum_connect_any:
connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1] connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1]
HdlSome: HdlSome:
wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1] wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1]
wire _cast_bits_to_bundle_expr_1: Ty5
wire _cast_bits_to_bundle_expr_flattened_1: Ty6
connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 2, 0), 0, 0)
wire _cast_bits_to_enum_expr_1: Ty3
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_1.tag, 0)):
connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlNone)
else:
connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlSome)
connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_enum_expr_1
connect _cast_bits_to_bundle_expr_flattened_1.body, bits(bits(i2.body, 2, 0), 2, 1)
connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body
; connect different types: ; connect different types:
; lhs: SInt<1> ; lhs: SInt<1>
; rhs: SInt<2> ; rhs: SInt<2>
connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1] connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr_1.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1]
wire _bundle_literal_expr_2: Ty4 wire _bundle_literal_expr_2: Ty4
connect _bundle_literal_expr_2.tag, {|HdlNone, HdlSome|}(HdlSome) connect _bundle_literal_expr_2.tag, {|HdlNone, HdlSome|}(HdlSome)
connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2) connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2)
@ -3964,18 +3599,18 @@ circuit check_enum_connect_any:
connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1] connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1]
C: C:
wire __connect_variant_body_3: Ty8 @[module-XXXXXXXXXX.rs 8:1] wire __connect_variant_body_3: Ty8 @[module-XXXXXXXXXX.rs 8:1]
wire _cast_bits_to_bundle_expr_1: Ty8 wire _cast_bits_to_bundle_expr_2: Ty8
wire _cast_bits_to_bundle_expr_flattened_1: Ty9 wire _cast_bits_to_bundle_expr_flattened_2: Ty9
connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 0, 0), 0, 0) connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i2.body, 0, 0), 0, 0)
wire _cast_bits_to_enum_expr_1: Ty3 wire _cast_bits_to_enum_expr_2: Ty3
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_1.tag, 0)): when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_2.tag, 0)):
connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlNone) connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlNone)
else: else:
connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlSome) connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlSome)
connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_enum_expr_1 connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_enum_expr_2
connect _cast_bits_to_bundle_expr_flattened_1.body, UInt<0>(0) connect _cast_bits_to_bundle_expr_flattened_2.body, UInt<0>(0)
connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body
connect __connect_variant_body_3, _cast_bits_to_bundle_expr_1 @[module-XXXXXXXXXX.rs 8:1] connect __connect_variant_body_3, _cast_bits_to_bundle_expr_2 @[module-XXXXXXXXXX.rs 8:1]
wire _bundle_literal_expr_4: Ty1 wire _bundle_literal_expr_4: Ty1
connect _bundle_literal_expr_4.tag, {|A, B, C|}(C) connect _bundle_literal_expr_4.tag, {|A, B, C|}(C)
wire _cast_bundle_to_bits_expr_1: Ty9 wire _cast_bundle_to_bits_expr_1: Ty9
@ -4004,18 +3639,18 @@ circuit check_enum_connect_any:
connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1] connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1]
B: B:
wire __connect_variant_body_5: Ty5 @[module-XXXXXXXXXX.rs 9:1] wire __connect_variant_body_5: Ty5 @[module-XXXXXXXXXX.rs 9:1]
wire _cast_bits_to_bundle_expr_2: Ty4 wire _cast_bits_to_bundle_expr_3: Ty4
wire _cast_bits_to_bundle_expr_flattened_2: Ty7 wire _cast_bits_to_bundle_expr_flattened_3: Ty7
connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i1.body, 1, 0), 0, 0) connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 1, 0), 0, 0)
wire _cast_bits_to_enum_expr_2: Ty3 wire _cast_bits_to_enum_expr_3: Ty3
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_2.tag, 0)): when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_3.tag, 0)):
connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlNone) connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlNone)
else: else:
connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlSome) connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlSome)
connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_enum_expr_2 connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_enum_expr_3
connect _cast_bits_to_bundle_expr_flattened_2.body, bits(bits(i1.body, 1, 0), 1, 1) connect _cast_bits_to_bundle_expr_flattened_3.body, bits(bits(i1.body, 1, 0), 1, 1)
connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body
match _cast_bits_to_bundle_expr_2.tag: @[module-XXXXXXXXXX.rs 9:1] match _cast_bits_to_bundle_expr_3.tag: @[module-XXXXXXXXXX.rs 9:1]
HdlNone: HdlNone:
wire _bundle_literal_expr_6: Ty5 wire _bundle_literal_expr_6: Ty5
connect _bundle_literal_expr_6.tag, {|HdlNone, HdlSome|}(HdlNone) connect _bundle_literal_expr_6.tag, {|HdlNone, HdlSome|}(HdlNone)
@ -4023,10 +3658,21 @@ circuit check_enum_connect_any:
connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1] connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1]
HdlSome: HdlSome:
wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1] wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1]
wire _cast_bits_to_bundle_expr_4: Ty4
wire _cast_bits_to_bundle_expr_flattened_4: Ty7
connect _cast_bits_to_bundle_expr_flattened_4.tag, bits(bits(i1.body, 1, 0), 0, 0)
wire _cast_bits_to_enum_expr_4: Ty3
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_4.tag, 0)):
connect _cast_bits_to_enum_expr_4, {|HdlNone, HdlSome|}(HdlNone)
else:
connect _cast_bits_to_enum_expr_4, {|HdlNone, HdlSome|}(HdlSome)
connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_enum_expr_4
connect _cast_bits_to_bundle_expr_flattened_4.body, bits(bits(i1.body, 1, 0), 1, 1)
connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_4.body
; connect different types: ; connect different types:
; lhs: SInt<2> ; lhs: SInt<2>
; rhs: SInt<1> ; rhs: SInt<1>
connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_2.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1] connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_4.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1]
wire _bundle_literal_expr_7: Ty5 wire _bundle_literal_expr_7: Ty5
connect _bundle_literal_expr_7.tag, {|HdlNone, HdlSome|}(HdlSome) connect _bundle_literal_expr_7.tag, {|HdlNone, HdlSome|}(HdlSome)
connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6) connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6)
@ -4048,18 +3694,18 @@ circuit check_enum_connect_any:
connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1] connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1]
C: C:
wire __connect_variant_body_7: Ty8 @[module-XXXXXXXXXX.rs 9:1] wire __connect_variant_body_7: Ty8 @[module-XXXXXXXXXX.rs 9:1]
wire _cast_bits_to_bundle_expr_3: Ty8 wire _cast_bits_to_bundle_expr_5: Ty8
wire _cast_bits_to_bundle_expr_flattened_3: Ty9 wire _cast_bits_to_bundle_expr_flattened_5: Ty9
connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 0, 0), 0, 0) connect _cast_bits_to_bundle_expr_flattened_5.tag, bits(bits(i1.body, 0, 0), 0, 0)
wire _cast_bits_to_enum_expr_3: Ty3 wire _cast_bits_to_enum_expr_5: Ty3
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_3.tag, 0)): when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_5.tag, 0)):
connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlNone) connect _cast_bits_to_enum_expr_5, {|HdlNone, HdlSome|}(HdlNone)
else: else:
connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlSome) connect _cast_bits_to_enum_expr_5, {|HdlNone, HdlSome|}(HdlSome)
connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_enum_expr_3 connect _cast_bits_to_bundle_expr_5.tag, _cast_bits_to_enum_expr_5
connect _cast_bits_to_bundle_expr_flattened_3.body, UInt<0>(0) connect _cast_bits_to_bundle_expr_flattened_5.body, UInt<0>(0)
connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body connect _cast_bits_to_bundle_expr_5.body, _cast_bits_to_bundle_expr_flattened_5.body
connect __connect_variant_body_7, _cast_bits_to_bundle_expr_3 @[module-XXXXXXXXXX.rs 9:1] connect __connect_variant_body_7, _cast_bits_to_bundle_expr_5 @[module-XXXXXXXXXX.rs 9:1]
wire _bundle_literal_expr_9: Ty2 wire _bundle_literal_expr_9: Ty2
connect _bundle_literal_expr_9.tag, {|A, B, C|}(C) connect _bundle_literal_expr_9.tag, {|A, B, C|}(C)
wire _cast_bundle_to_bits_expr_3: Ty9 wire _cast_bundle_to_bits_expr_3: Ty9
@ -4126,10 +3772,16 @@ circuit check_enum_connect_any:
connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1] connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1]
else: else:
wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1] wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1]
wire _cast_bits_to_bundle_expr_1: Ty3
wire _cast_bits_to_bundle_expr_flattened_1: Ty3
connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 2, 0), 0, 0)
connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_bundle_expr_flattened_1.tag
connect _cast_bits_to_bundle_expr_flattened_1.body, bits(bits(i2.body, 2, 0), 2, 1)
connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body
; connect different types: ; connect different types:
; lhs: SInt<1> ; lhs: SInt<1>
; rhs: SInt<2> ; rhs: SInt<2>
connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1] connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr_1.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1]
wire _bundle_literal_expr_2: Ty2 wire _bundle_literal_expr_2: Ty2
connect _bundle_literal_expr_2.tag, UInt<1>(0h1) connect _bundle_literal_expr_2.tag, UInt<1>(0h1)
connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2) connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2)
@ -4145,13 +3797,13 @@ circuit check_enum_connect_any:
connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1] connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1]
else: else:
wire __connect_variant_body_3: Ty4 @[module-XXXXXXXXXX.rs 8:1] wire __connect_variant_body_3: Ty4 @[module-XXXXXXXXXX.rs 8:1]
wire _cast_bits_to_bundle_expr_1: Ty4 wire _cast_bits_to_bundle_expr_2: Ty4
wire _cast_bits_to_bundle_expr_flattened_1: Ty4 wire _cast_bits_to_bundle_expr_flattened_2: Ty4
connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 0, 0), 0, 0) connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i2.body, 0, 0), 0, 0)
connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_bundle_expr_flattened_1.tag connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_bundle_expr_flattened_2.tag
connect _cast_bits_to_bundle_expr_flattened_1.body, UInt<0>(0) connect _cast_bits_to_bundle_expr_flattened_2.body, UInt<0>(0)
connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body
connect __connect_variant_body_3, _cast_bits_to_bundle_expr_1 @[module-XXXXXXXXXX.rs 8:1] connect __connect_variant_body_3, _cast_bits_to_bundle_expr_2 @[module-XXXXXXXXXX.rs 8:1]
wire _bundle_literal_expr_4: Ty0 wire _bundle_literal_expr_4: Ty0
connect _bundle_literal_expr_4.tag, UInt<2>(0h2) connect _bundle_literal_expr_4.tag, UInt<2>(0h2)
wire _cast_bundle_to_bits_expr_1: Ty4 wire _cast_bundle_to_bits_expr_1: Ty4
@ -4173,23 +3825,29 @@ circuit check_enum_connect_any:
connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1] connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1]
else when eq(i1.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 9:1] else when eq(i1.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 9:1]
wire __connect_variant_body_5: Ty3 @[module-XXXXXXXXXX.rs 9:1] wire __connect_variant_body_5: Ty3 @[module-XXXXXXXXXX.rs 9:1]
wire _cast_bits_to_bundle_expr_2: Ty2 wire _cast_bits_to_bundle_expr_3: Ty2
wire _cast_bits_to_bundle_expr_flattened_2: Ty2 wire _cast_bits_to_bundle_expr_flattened_3: Ty2
connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i1.body, 1, 0), 0, 0) connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 1, 0), 0, 0)
connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_bundle_expr_flattened_2.tag connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_bundle_expr_flattened_3.tag
connect _cast_bits_to_bundle_expr_flattened_2.body, bits(bits(i1.body, 1, 0), 1, 1) connect _cast_bits_to_bundle_expr_flattened_3.body, bits(bits(i1.body, 1, 0), 1, 1)
connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body
when eq(_cast_bits_to_bundle_expr_2.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 9:1] when eq(_cast_bits_to_bundle_expr_3.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 9:1]
wire _bundle_literal_expr_6: Ty3 wire _bundle_literal_expr_6: Ty3
connect _bundle_literal_expr_6.tag, UInt<1>(0h0) connect _bundle_literal_expr_6.tag, UInt<1>(0h0)
connect _bundle_literal_expr_6.body, UInt<2>(0h0) connect _bundle_literal_expr_6.body, UInt<2>(0h0)
connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1] connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1]
else: else:
wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1] wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1]
wire _cast_bits_to_bundle_expr_4: Ty2
wire _cast_bits_to_bundle_expr_flattened_4: Ty2
connect _cast_bits_to_bundle_expr_flattened_4.tag, bits(bits(i1.body, 1, 0), 0, 0)
connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_bundle_expr_flattened_4.tag
connect _cast_bits_to_bundle_expr_flattened_4.body, bits(bits(i1.body, 1, 0), 1, 1)
connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_4.body
; connect different types: ; connect different types:
; lhs: SInt<2> ; lhs: SInt<2>
; rhs: SInt<1> ; rhs: SInt<1>
connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_2.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1] connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_4.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1]
wire _bundle_literal_expr_7: Ty3 wire _bundle_literal_expr_7: Ty3
connect _bundle_literal_expr_7.tag, UInt<1>(0h1) connect _bundle_literal_expr_7.tag, UInt<1>(0h1)
connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6) connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6)
@ -4205,13 +3863,13 @@ circuit check_enum_connect_any:
connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1] connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1]
else: else:
wire __connect_variant_body_7: Ty4 @[module-XXXXXXXXXX.rs 9:1] wire __connect_variant_body_7: Ty4 @[module-XXXXXXXXXX.rs 9:1]
wire _cast_bits_to_bundle_expr_3: Ty4 wire _cast_bits_to_bundle_expr_5: Ty4
wire _cast_bits_to_bundle_expr_flattened_3: Ty4 wire _cast_bits_to_bundle_expr_flattened_5: Ty4
connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 0, 0), 0, 0) connect _cast_bits_to_bundle_expr_flattened_5.tag, bits(bits(i1.body, 0, 0), 0, 0)
connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_bundle_expr_flattened_3.tag connect _cast_bits_to_bundle_expr_5.tag, _cast_bits_to_bundle_expr_flattened_5.tag
connect _cast_bits_to_bundle_expr_flattened_3.body, UInt<0>(0) connect _cast_bits_to_bundle_expr_flattened_5.body, UInt<0>(0)
connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body connect _cast_bits_to_bundle_expr_5.body, _cast_bits_to_bundle_expr_flattened_5.body
connect __connect_variant_body_7, _cast_bits_to_bundle_expr_3 @[module-XXXXXXXXXX.rs 9:1] connect __connect_variant_body_7, _cast_bits_to_bundle_expr_5 @[module-XXXXXXXXXX.rs 9:1]
wire _bundle_literal_expr_9: Ty1 wire _bundle_literal_expr_9: Ty1
connect _bundle_literal_expr_9.tag, UInt<2>(0h2) connect _bundle_literal_expr_9.tag, UInt<2>(0h2)
wire _cast_bundle_to_bits_expr_3: Ty4 wire _cast_bundle_to_bits_expr_3: Ty4
@ -4863,20 +4521,34 @@ circuit check_struct_cmp_eq:
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1] input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1] output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1] output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
wire _bundle_structural_eq: UInt<1> wire _array_literal_expr: UInt<1>[3]
connect _bundle_structural_eq, and(eq(tuple_lhs.`0`, tuple_rhs.`0`), eq(tuple_lhs.`1`, tuple_rhs.`1`)) connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`)
wire _bundle_structural_eq_1: UInt<1> connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`)
connect _bundle_structural_eq_1, and(_bundle_structural_eq, eq(tuple_lhs.`2`, tuple_rhs.`2`)) connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`)
connect tuple_cmp_eq, _bundle_structural_eq_1 @[module-XXXXXXXXXX.rs 5:1] wire _cast_array_to_bits_expr: UInt<1>[3]
connect tuple_cmp_ne, not(_bundle_structural_eq_1) @[module-XXXXXXXXXX.rs 7:1] connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
wire _bundle_structural_eq_2: UInt<1> connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
connect _bundle_structural_eq_2, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
connect test_struct_cmp_eq, _bundle_structural_eq_2 @[module-XXXXXXXXXX.rs 11:1] wire _cast_to_bits_expr: UInt<3>
connect test_struct_cmp_ne, not(_bundle_structural_eq_2) @[module-XXXXXXXXXX.rs 13:1] connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
wire _array_literal_expr_1: UInt<1>[3]
connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`)
connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`)
connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`)
wire _cast_array_to_bits_expr_1: UInt<1>[3]
connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0]
connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1]
connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2]
wire _cast_to_bits_expr_1: UInt<3>
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1]
connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1]
connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1]
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1] connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
connect test_struct_2_cmp_ne, not(eq(test_struct_2_lhs.v, test_struct_2_rhs.v)) @[module-XXXXXXXXXX.rs 19:1] connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1]
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1] connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
connect test_struct_3_cmp_ne, not(UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 25:1] connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1]
", ",
}; };
} }
@ -4959,55 +4631,3 @@ 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]
",
};
}

File diff suppressed because it is too large Load diff

View file

@ -419,7 +419,6 @@ Simulation {
}, },
pc: 38, pc: 38,
memory_write_log: [], memory_write_log: [],
assert_failed_log: [],
memories: StatePart { memories: StatePart {
value: [], value: [],
}, },
@ -498,7 +497,6 @@ Simulation {
.. ..
}, },
}, },
global_io: {},
main_module: SimulationModuleState { main_module: SimulationModuleState {
base_targets: [ base_targets: [
Instance { Instance {
@ -828,9 +826,9 @@ Simulation {
}.write_index, }.write_index,
}, },
did_initial_settle: true, did_initial_settle: true,
clocks_for_past: {},
}, },
extern_modules: [], extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule { trace_decls: TraceModule {
name: "array_rw", name: "array_rw",
children: [ children: [
@ -1220,7 +1218,6 @@ Simulation {
index: StatePartIndex<BigSlots>(0), index: StatePartIndex<BigSlots>(0),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xff, state: 0xff,
last_state: 0xff, last_state: 0xff,
}, },
@ -1230,7 +1227,6 @@ Simulation {
index: StatePartIndex<BigSlots>(1), index: StatePartIndex<BigSlots>(1),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x7f, state: 0x7f,
last_state: 0x7f, last_state: 0x7f,
}, },
@ -1240,7 +1236,6 @@ Simulation {
index: StatePartIndex<BigSlots>(2), index: StatePartIndex<BigSlots>(2),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x3f, state: 0x3f,
last_state: 0x3f, last_state: 0x3f,
}, },
@ -1250,7 +1245,6 @@ Simulation {
index: StatePartIndex<BigSlots>(3), index: StatePartIndex<BigSlots>(3),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x1f, state: 0x1f,
last_state: 0x1f, last_state: 0x1f,
}, },
@ -1260,7 +1254,6 @@ Simulation {
index: StatePartIndex<BigSlots>(4), index: StatePartIndex<BigSlots>(4),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x0f, state: 0x0f,
last_state: 0x0f, last_state: 0x0f,
}, },
@ -1270,7 +1263,6 @@ Simulation {
index: StatePartIndex<BigSlots>(5), index: StatePartIndex<BigSlots>(5),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x07, state: 0x07,
last_state: 0x07, last_state: 0x07,
}, },
@ -1280,7 +1272,6 @@ Simulation {
index: StatePartIndex<BigSlots>(6), index: StatePartIndex<BigSlots>(6),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x03, state: 0x03,
last_state: 0x03, last_state: 0x03,
}, },
@ -1290,7 +1281,6 @@ Simulation {
index: StatePartIndex<BigSlots>(7), index: StatePartIndex<BigSlots>(7),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x01, state: 0x01,
last_state: 0x01, last_state: 0x01,
}, },
@ -1300,7 +1290,6 @@ Simulation {
index: StatePartIndex<BigSlots>(8), index: StatePartIndex<BigSlots>(8),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x00, state: 0x00,
last_state: 0x00, last_state: 0x00,
}, },
@ -1310,7 +1299,6 @@ Simulation {
index: StatePartIndex<BigSlots>(9), index: StatePartIndex<BigSlots>(9),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x80, state: 0x80,
last_state: 0x80, last_state: 0x80,
}, },
@ -1320,7 +1308,6 @@ Simulation {
index: StatePartIndex<BigSlots>(10), index: StatePartIndex<BigSlots>(10),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xc0, state: 0xc0,
last_state: 0xc0, last_state: 0xc0,
}, },
@ -1330,7 +1317,6 @@ Simulation {
index: StatePartIndex<BigSlots>(11), index: StatePartIndex<BigSlots>(11),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xe0, state: 0xe0,
last_state: 0xe0, last_state: 0xe0,
}, },
@ -1340,7 +1326,6 @@ Simulation {
index: StatePartIndex<BigSlots>(12), index: StatePartIndex<BigSlots>(12),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xf0, state: 0xf0,
last_state: 0xf0, last_state: 0xf0,
}, },
@ -1350,7 +1335,6 @@ Simulation {
index: StatePartIndex<BigSlots>(13), index: StatePartIndex<BigSlots>(13),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xf8, state: 0xf8,
last_state: 0xf8, last_state: 0xf8,
}, },
@ -1360,7 +1344,6 @@ Simulation {
index: StatePartIndex<BigSlots>(14), index: StatePartIndex<BigSlots>(14),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xfc, state: 0xfc,
last_state: 0xfc, last_state: 0xfc,
}, },
@ -1370,7 +1353,6 @@ Simulation {
index: StatePartIndex<BigSlots>(15), index: StatePartIndex<BigSlots>(15),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xfe, state: 0xfe,
last_state: 0xfe, last_state: 0xfe,
}, },
@ -1380,7 +1362,6 @@ Simulation {
index: StatePartIndex<BigSlots>(16), index: StatePartIndex<BigSlots>(16),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xff, state: 0xff,
last_state: 0xff, last_state: 0xff,
}, },
@ -1390,7 +1371,6 @@ Simulation {
index: StatePartIndex<BigSlots>(17), index: StatePartIndex<BigSlots>(17),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x7f, state: 0x7f,
last_state: 0x7f, last_state: 0x7f,
}, },
@ -1400,7 +1380,6 @@ Simulation {
index: StatePartIndex<BigSlots>(18), index: StatePartIndex<BigSlots>(18),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x3f, state: 0x3f,
last_state: 0x3f, last_state: 0x3f,
}, },
@ -1410,7 +1389,6 @@ Simulation {
index: StatePartIndex<BigSlots>(19), index: StatePartIndex<BigSlots>(19),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x1f, state: 0x1f,
last_state: 0x1f, last_state: 0x1f,
}, },
@ -1420,7 +1398,6 @@ Simulation {
index: StatePartIndex<BigSlots>(20), index: StatePartIndex<BigSlots>(20),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x0f, state: 0x0f,
last_state: 0x0f, last_state: 0x0f,
}, },
@ -1430,7 +1407,6 @@ Simulation {
index: StatePartIndex<BigSlots>(21), index: StatePartIndex<BigSlots>(21),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x07, state: 0x07,
last_state: 0x07, last_state: 0x07,
}, },
@ -1440,7 +1416,6 @@ Simulation {
index: StatePartIndex<BigSlots>(22), index: StatePartIndex<BigSlots>(22),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x03, state: 0x03,
last_state: 0x03, last_state: 0x03,
}, },
@ -1450,7 +1425,6 @@ Simulation {
index: StatePartIndex<BigSlots>(23), index: StatePartIndex<BigSlots>(23),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x01, state: 0x01,
last_state: 0x01, last_state: 0x01,
}, },
@ -1460,7 +1434,6 @@ Simulation {
index: StatePartIndex<BigSlots>(24), index: StatePartIndex<BigSlots>(24),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x00, state: 0x00,
last_state: 0x00, last_state: 0x00,
}, },
@ -1470,7 +1443,6 @@ Simulation {
index: StatePartIndex<BigSlots>(25), index: StatePartIndex<BigSlots>(25),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x80, state: 0x80,
last_state: 0x80, last_state: 0x80,
}, },
@ -1480,7 +1452,6 @@ Simulation {
index: StatePartIndex<BigSlots>(26), index: StatePartIndex<BigSlots>(26),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xc0, state: 0xc0,
last_state: 0xc0, last_state: 0xc0,
}, },
@ -1490,7 +1461,6 @@ Simulation {
index: StatePartIndex<BigSlots>(27), index: StatePartIndex<BigSlots>(27),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xe0, state: 0xe0,
last_state: 0xe0, last_state: 0xe0,
}, },
@ -1500,7 +1470,6 @@ Simulation {
index: StatePartIndex<BigSlots>(28), index: StatePartIndex<BigSlots>(28),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xf0, state: 0xf0,
last_state: 0xf0, last_state: 0xf0,
}, },
@ -1510,7 +1479,6 @@ Simulation {
index: StatePartIndex<BigSlots>(29), index: StatePartIndex<BigSlots>(29),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xf8, state: 0xf8,
last_state: 0xf8, last_state: 0xf8,
}, },
@ -1520,7 +1488,6 @@ Simulation {
index: StatePartIndex<BigSlots>(30), index: StatePartIndex<BigSlots>(30),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xfc, state: 0xfc,
last_state: 0xfc, last_state: 0xfc,
}, },
@ -1530,7 +1497,6 @@ Simulation {
index: StatePartIndex<BigSlots>(31), index: StatePartIndex<BigSlots>(31),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xfe, state: 0xfe,
last_state: 0xe1, last_state: 0xe1,
}, },
@ -1540,7 +1506,6 @@ Simulation {
index: StatePartIndex<BigSlots>(32), index: StatePartIndex<BigSlots>(32),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x00, state: 0x00,
last_state: 0x00, last_state: 0x00,
}, },
@ -1550,7 +1515,6 @@ Simulation {
index: StatePartIndex<BigSlots>(33), index: StatePartIndex<BigSlots>(33),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xff, state: 0xff,
last_state: 0xff, last_state: 0xff,
}, },
@ -1560,7 +1524,6 @@ Simulation {
index: StatePartIndex<BigSlots>(34), index: StatePartIndex<BigSlots>(34),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x10, state: 0x10,
last_state: 0x0f, last_state: 0x0f,
}, },
@ -1570,7 +1533,6 @@ Simulation {
index: StatePartIndex<BigSlots>(35), index: StatePartIndex<BigSlots>(35),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x00, state: 0x00,
last_state: 0xe1, last_state: 0xe1,
}, },
@ -1579,7 +1541,6 @@ Simulation {
kind: BigBool { kind: BigBool {
index: StatePartIndex<BigSlots>(36), index: StatePartIndex<BigSlots>(36),
}, },
maybe_changed: true,
state: 0x1, state: 0x1,
last_state: 0x1, last_state: 0x1,
}, },
@ -1589,7 +1550,6 @@ Simulation {
index: StatePartIndex<BigSlots>(37), index: StatePartIndex<BigSlots>(37),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xff, state: 0xff,
last_state: 0xff, last_state: 0xff,
}, },
@ -1599,7 +1559,6 @@ Simulation {
index: StatePartIndex<BigSlots>(38), index: StatePartIndex<BigSlots>(38),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x7f, state: 0x7f,
last_state: 0x7f, last_state: 0x7f,
}, },
@ -1609,7 +1568,6 @@ Simulation {
index: StatePartIndex<BigSlots>(39), index: StatePartIndex<BigSlots>(39),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x3f, state: 0x3f,
last_state: 0x3f, last_state: 0x3f,
}, },
@ -1619,7 +1577,6 @@ Simulation {
index: StatePartIndex<BigSlots>(40), index: StatePartIndex<BigSlots>(40),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x1f, state: 0x1f,
last_state: 0x1f, last_state: 0x1f,
}, },
@ -1629,7 +1586,6 @@ Simulation {
index: StatePartIndex<BigSlots>(41), index: StatePartIndex<BigSlots>(41),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x0f, state: 0x0f,
last_state: 0x0f, last_state: 0x0f,
}, },
@ -1639,7 +1595,6 @@ Simulation {
index: StatePartIndex<BigSlots>(42), index: StatePartIndex<BigSlots>(42),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x07, state: 0x07,
last_state: 0x07, last_state: 0x07,
}, },
@ -1649,7 +1604,6 @@ Simulation {
index: StatePartIndex<BigSlots>(43), index: StatePartIndex<BigSlots>(43),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x03, state: 0x03,
last_state: 0x03, last_state: 0x03,
}, },
@ -1659,7 +1613,6 @@ Simulation {
index: StatePartIndex<BigSlots>(44), index: StatePartIndex<BigSlots>(44),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x01, state: 0x01,
last_state: 0x01, last_state: 0x01,
}, },
@ -1669,7 +1622,6 @@ Simulation {
index: StatePartIndex<BigSlots>(45), index: StatePartIndex<BigSlots>(45),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x00, state: 0x00,
last_state: 0x00, last_state: 0x00,
}, },
@ -1679,7 +1631,6 @@ Simulation {
index: StatePartIndex<BigSlots>(46), index: StatePartIndex<BigSlots>(46),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x80, state: 0x80,
last_state: 0x80, last_state: 0x80,
}, },
@ -1689,7 +1640,6 @@ Simulation {
index: StatePartIndex<BigSlots>(47), index: StatePartIndex<BigSlots>(47),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xc0, state: 0xc0,
last_state: 0xc0, last_state: 0xc0,
}, },
@ -1699,7 +1649,6 @@ Simulation {
index: StatePartIndex<BigSlots>(48), index: StatePartIndex<BigSlots>(48),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xe0, state: 0xe0,
last_state: 0xe0, last_state: 0xe0,
}, },
@ -1709,7 +1658,6 @@ Simulation {
index: StatePartIndex<BigSlots>(49), index: StatePartIndex<BigSlots>(49),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xf0, state: 0xf0,
last_state: 0xf0, last_state: 0xf0,
}, },
@ -1719,7 +1667,6 @@ Simulation {
index: StatePartIndex<BigSlots>(50), index: StatePartIndex<BigSlots>(50),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xf8, state: 0xf8,
last_state: 0xf8, last_state: 0xf8,
}, },
@ -1729,7 +1676,6 @@ Simulation {
index: StatePartIndex<BigSlots>(51), index: StatePartIndex<BigSlots>(51),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xfc, state: 0xfc,
last_state: 0xfc, last_state: 0xfc,
}, },
@ -1739,7 +1685,6 @@ Simulation {
index: StatePartIndex<BigSlots>(52), index: StatePartIndex<BigSlots>(52),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0xfe, state: 0xfe,
last_state: 0xe1, last_state: 0xe1,
}, },
@ -1754,13 +1699,7 @@ Simulation {
}, },
), ),
], ],
clocks_triggered: [],
event_queue: EventQueue(EventQueueData {
instant: 34 μs, instant: 34 μs,
events: {}, clocks_triggered: [],
}),
waiting_sensitivity_sets_by_address: {},
waiting_sensitivity_sets_by_compiled_value: {},
asserts: [],
.. ..
} }

View file

@ -1,283 +1,283 @@
$timescale 1 ps $end $timescale 1 ps $end
$scope module array_rw $end $scope module array_rw $end
$scope struct array_in $end $scope struct array_in $end
$var wire 8 Yvfu^ \[0] $end $var wire 8 ! \[0] $end
$var wire 8 |Cs`W \[1] $end $var wire 8 " \[1] $end
$var wire 8 M!nsb \[2] $end $var wire 8 # \[2] $end
$var wire 8 59L{w \[3] $end $var wire 8 $ \[3] $end
$var wire 8 o2+|F \[4] $end $var wire 8 % \[4] $end
$var wire 8 ikzV5 \[5] $end $var wire 8 & \[5] $end
$var wire 8 [E$Z* \[6] $end $var wire 8 ' \[6] $end
$var wire 8 ?"~01 \[7] $end $var wire 8 ( \[7] $end
$var wire 8 /kghT \[8] $end $var wire 8 ) \[8] $end
$var wire 8 +}(9) \[9] $end $var wire 8 * \[9] $end
$var wire 8 iMP}= \[10] $end $var wire 8 + \[10] $end
$var wire 8 2M0tL \[11] $end $var wire 8 , \[11] $end
$var wire 8 :AjkA \[12] $end $var wire 8 - \[12] $end
$var wire 8 VM_:8 \[13] $end $var wire 8 . \[13] $end
$var wire 8 UveL2 \[14] $end $var wire 8 / \[14] $end
$var wire 8 A)9Z6 \[15] $end $var wire 8 0 \[15] $end
$upscope $end $upscope $end
$scope struct array_out $end $scope struct array_out $end
$var wire 8 2zdj1 \[0] $end $var wire 8 1 \[0] $end
$var wire 8 =;m_[ \[1] $end $var wire 8 2 \[1] $end
$var wire 8 @9Hd \[2] $end $var wire 8 3 \[2] $end
$var wire 8 C:="| \[3] $end $var wire 8 4 \[3] $end
$var wire 8 IDk7# \[4] $end $var wire 8 5 \[4] $end
$var wire 8 i]E1i \[5] $end $var wire 8 6 \[5] $end
$var wire 8 tK,M] \[6] $end $var wire 8 7 \[6] $end
$var wire 8 tGp!\ \[7] $end $var wire 8 8 \[7] $end
$var wire 8 ."qjK \[8] $end $var wire 8 9 \[8] $end
$var wire 8 AUO:R \[9] $end $var wire 8 : \[9] $end
$var wire 8 'kx`n \[10] $end $var wire 8 ; \[10] $end
$var wire 8 U&(K\ \[11] $end $var wire 8 < \[11] $end
$var wire 8 q<O41 \[12] $end $var wire 8 = \[12] $end
$var wire 8 zvj)] \[13] $end $var wire 8 > \[13] $end
$var wire 8 >0H<( \[14] $end $var wire 8 ? \[14] $end
$var wire 8 ARhXJ \[15] $end $var wire 8 @ \[15] $end
$upscope $end $upscope $end
$var wire 8 -n:7@ read_index $end $var wire 8 A read_index $end
$var wire 8 >h<=Z read_data $end $var wire 8 B read_data $end
$var wire 8 [xld3 write_index $end $var wire 8 C write_index $end
$var wire 8 J+DYh write_data $end $var wire 8 D write_data $end
$var wire 1 z,@WW write_en $end $var wire 1 E write_en $end
$scope struct array_wire $end $scope struct array_wire $end
$var wire 8 B{KJS \[0] $end $var wire 8 F \[0] $end
$var wire 8 V'K*& \[1] $end $var wire 8 G \[1] $end
$var wire 8 4zI$O \[2] $end $var wire 8 H \[2] $end
$var wire 8 %TTk[ \[3] $end $var wire 8 I \[3] $end
$var wire 8 IgSeY \[4] $end $var wire 8 J \[4] $end
$var wire 8 &&1T" \[5] $end $var wire 8 K \[5] $end
$var wire 8 5)-l\ \[6] $end $var wire 8 L \[6] $end
$var wire 8 0RsLb \[7] $end $var wire 8 M \[7] $end
$var wire 8 T>:}D \[8] $end $var wire 8 N \[8] $end
$var wire 8 DPpZ* \[9] $end $var wire 8 O \[9] $end
$var wire 8 %E(nf \[10] $end $var wire 8 P \[10] $end
$var wire 8 2'pba \[11] $end $var wire 8 Q \[11] $end
$var wire 8 e/c1: \[12] $end $var wire 8 R \[12] $end
$var wire 8 ;w.C7 \[13] $end $var wire 8 S \[13] $end
$var wire 8 fwdfu \[14] $end $var wire 8 T \[14] $end
$var wire 8 *R\vx \[15] $end $var wire 8 U \[15] $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$enddefinitions $end $enddefinitions $end
$dumpvars $dumpvars
b11111111 Yvfu^ b11111111 !
b1111111 |Cs`W b1111111 "
b111111 M!nsb b111111 #
b11111 59L{w b11111 $
b1111 o2+|F b1111 %
b111 ikzV5 b111 &
b11 [E$Z* b11 '
b1 ?"~01 b1 (
b0 /kghT b0 )
b10000000 +}(9) b10000000 *
b11000000 iMP}= b11000000 +
b11100000 2M0tL b11100000 ,
b11110000 :AjkA b11110000 -
b11111000 VM_:8 b11111000 .
b11111100 UveL2 b11111100 /
b11111110 A)9Z6 b11111110 0
b11111111 2zdj1 b11111111 1
b1111111 =;m_[ b1111111 2
b111111 @9Hd b111111 3
b11111 C:="| b11111 4
b1111 IDk7# b1111 5
b111 i]E1i b111 6
b11 tK,M] b11 7
b1 tGp!\ b1 8
b0 ."qjK b0 9
b10000000 AUO:R b10000000 :
b11000000 'kx`n b11000000 ;
b11100000 U&(K\ b11100000 <
b11110000 q<O41 b11110000 =
b11111000 zvj)] b11111000 >
b11111100 >0H<( b11111100 ?
b11111110 ARhXJ b11111110 @
b0 -n:7@ b0 A
b11111111 >h<=Z b11111111 B
b0 [xld3 b0 C
b0 J+DYh b0 D
0z,@WW 0E
b11111111 B{KJS b11111111 F
b1111111 V'K*& b1111111 G
b111111 4zI$O b111111 H
b11111 %TTk[ b11111 I
b1111 IgSeY b1111 J
b111 &&1T" b111 K
b11 5)-l\ b11 L
b1 0RsLb b1 M
b0 T>:}D b0 N
b10000000 DPpZ* b10000000 O
b11000000 %E(nf b11000000 P
b11100000 2'pba b11100000 Q
b11110000 e/c1: b11110000 R
b11111000 ;w.C7 b11111000 S
b11111100 fwdfu b11111100 T
b11111110 *R\vx b11111110 U
$end $end
#1000000 #1000000
b1 -n:7@ b1 A
b1111111 >h<=Z b1111111 B
#2000000 #2000000
b10 -n:7@ b10 A
b111111 >h<=Z b111111 B
#3000000 #3000000
b11 -n:7@ b11 A
b11111 >h<=Z b11111 B
#4000000 #4000000
b100 -n:7@ b100 A
b1111 >h<=Z b1111 B
#5000000 #5000000
b101 -n:7@ b101 A
b111 >h<=Z b111 B
#6000000 #6000000
b110 -n:7@ b110 A
b11 >h<=Z b11 B
#7000000 #7000000
b111 -n:7@ b111 A
b1 >h<=Z b1 B
#8000000 #8000000
b1000 -n:7@ b1000 A
b0 >h<=Z b0 B
#9000000 #9000000
b1001 -n:7@ b1001 A
b10000000 >h<=Z b10000000 B
#10000000 #10000000
b1010 -n:7@ b1010 A
b11000000 >h<=Z b11000000 B
#11000000 #11000000
b1011 -n:7@ b1011 A
b11100000 >h<=Z b11100000 B
#12000000 #12000000
b1100 -n:7@ b1100 A
b11110000 >h<=Z b11110000 B
#13000000 #13000000
b1101 -n:7@ b1101 A
b11111000 >h<=Z b11111000 B
#14000000 #14000000
b1110 -n:7@ b1110 A
b11111100 >h<=Z b11111100 B
#15000000 #15000000
b1111 -n:7@ b1111 A
b11111110 >h<=Z b11111110 B
#16000000 #16000000
b10000 -n:7@ b10000 A
b0 >h<=Z b0 B
#17000000 #17000000
b0 2zdj1 b0 1
b0 -n:7@ b0 A
1z,@WW 1E
b0 B{KJS b0 F
#18000000 #18000000
b11111111 2zdj1 b11111111 1
b1 =;m_[ b1 2
b11111111 >h<=Z b11111111 B
b1 [xld3 b1 C
b1 J+DYh b1 D
b11111111 B{KJS b11111111 F
b1 V'K*& b1 G
#19000000 #19000000
b1111111 =;m_[ b1111111 2
b100 @9Hd b100 3
b10 [xld3 b10 C
b100 J+DYh b100 D
b1111111 V'K*& b1111111 G
b100 4zI$O b100 H
#20000000 #20000000
b111111 @9Hd b111111 3
b1001 C:="| b1001 4
b11 [xld3 b11 C
b1001 J+DYh b1001 D
b111111 4zI$O b111111 H
b1001 %TTk[ b1001 I
#21000000 #21000000
b11111 C:="| b11111 4
b10000 IDk7# b10000 5
b100 [xld3 b100 C
b10000 J+DYh b10000 D
b11111 %TTk[ b11111 I
b10000 IgSeY b10000 J
#22000000 #22000000
b1111 IDk7# b1111 5
b11001 i]E1i b11001 6
b101 [xld3 b101 C
b11001 J+DYh b11001 D
b1111 IgSeY b1111 J
b11001 &&1T" b11001 K
#23000000 #23000000
b111 i]E1i b111 6
b100100 tK,M] b100100 7
b110 [xld3 b110 C
b100100 J+DYh b100100 D
b111 &&1T" b111 K
b100100 5)-l\ b100100 L
#24000000 #24000000
b11 tK,M] b11 7
b110001 tGp!\ b110001 8
b111 [xld3 b111 C
b110001 J+DYh b110001 D
b11 5)-l\ b11 L
b110001 0RsLb b110001 M
#25000000 #25000000
b1 tGp!\ b1 8
b1000000 ."qjK b1000000 9
b1000 [xld3 b1000 C
b1000000 J+DYh b1000000 D
b1 0RsLb b1 M
b1000000 T>:}D b1000000 N
#26000000 #26000000
b0 ."qjK b0 9
b1010001 AUO:R b1010001 :
b1001 [xld3 b1001 C
b1010001 J+DYh b1010001 D
b0 T>:}D b0 N
b1010001 DPpZ* b1010001 O
#27000000 #27000000
b10000000 AUO:R b10000000 :
b1100100 'kx`n b1100100 ;
b1010 [xld3 b1010 C
b1100100 J+DYh b1100100 D
b10000000 DPpZ* b10000000 O
b1100100 %E(nf b1100100 P
#28000000 #28000000
b11000000 'kx`n b11000000 ;
b1111001 U&(K\ b1111001 <
b1011 [xld3 b1011 C
b1111001 J+DYh b1111001 D
b11000000 %E(nf b11000000 P
b1111001 2'pba b1111001 Q
#29000000 #29000000
b11100000 U&(K\ b11100000 <
b10010000 q<O41 b10010000 =
b1100 [xld3 b1100 C
b10010000 J+DYh b10010000 D
b11100000 2'pba b11100000 Q
b10010000 e/c1: b10010000 R
#30000000 #30000000
b11110000 q<O41 b11110000 =
b10101001 zvj)] b10101001 >
b1101 [xld3 b1101 C
b10101001 J+DYh b10101001 D
b11110000 e/c1: b11110000 R
b10101001 ;w.C7 b10101001 S
#31000000 #31000000
b11111000 zvj)] b11111000 >
b11000100 >0H<( b11000100 ?
b1110 [xld3 b1110 C
b11000100 J+DYh b11000100 D
b11111000 ;w.C7 b11111000 S
b11000100 fwdfu b11000100 T
#32000000 #32000000
b11111100 >0H<( b11111100 ?
b11100001 ARhXJ b11100001 @
b1111 [xld3 b1111 C
b11100001 J+DYh b11100001 D
b11111100 fwdfu b11111100 T
b11100001 *R\vx b11100001 U
#33000000 #33000000
b11111110 ARhXJ b11111110 @
b10000 [xld3 b10000 C
b0 J+DYh b0 D
b11111110 *R\vx b11111110 U
#34000000 #34000000

View file

@ -76,7 +76,6 @@ Simulation {
}, },
pc: 5, pc: 5,
memory_write_log: [], memory_write_log: [],
assert_failed_log: [],
memories: StatePart { memories: StatePart {
value: [], value: [],
}, },
@ -102,7 +101,6 @@ Simulation {
.. ..
}, },
}, },
global_io: {},
main_module: SimulationModuleState { main_module: SimulationModuleState {
base_targets: [ base_targets: [
Instance { Instance {
@ -124,9 +122,9 @@ Simulation {
}.i, }.i,
}, },
did_initial_settle: true, did_initial_settle: true,
clocks_for_past: {},
}, },
extern_modules: [], extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule { trace_decls: TraceModule {
name: "conditional_assignment_last", name: "conditional_assignment_last",
children: [ children: [
@ -157,7 +155,6 @@ Simulation {
kind: BigBool { kind: BigBool {
index: StatePartIndex<BigSlots>(0), index: StatePartIndex<BigSlots>(0),
}, },
maybe_changed: true,
state: 0x1, state: 0x1,
last_state: 0x0, last_state: 0x0,
}, },
@ -166,7 +163,6 @@ Simulation {
kind: BigBool { kind: BigBool {
index: StatePartIndex<BigSlots>(1), index: StatePartIndex<BigSlots>(1),
}, },
maybe_changed: true,
state: 0x0, state: 0x0,
last_state: 0x1, last_state: 0x1,
}, },
@ -181,13 +177,7 @@ Simulation {
}, },
), ),
], ],
clocks_triggered: [],
event_queue: EventQueue(EventQueueData {
instant: 2 μs, instant: 2 μs,
events: {}, clocks_triggered: [],
}),
waiting_sensitivity_sets_by_address: {},
waiting_sensitivity_sets_by_compiled_value: {},
asserts: [],
.. ..
} }

View file

@ -1,14 +1,14 @@
$timescale 1 ps $end $timescale 1 ps $end
$scope module conditional_assignment_last $end $scope module conditional_assignment_last $end
$var wire 1 xt~(W i $end $var wire 1 ! i $end
$var wire 1 6:7im w $end $var wire 1 " w $end
$upscope $end $upscope $end
$enddefinitions $end $enddefinitions $end
$dumpvars $dumpvars
0xt~(W 0!
16:7im 1"
$end $end
#1000000 #1000000
1xt~(W 1!
06:7im 0"
#2000000 #2000000

View file

@ -54,7 +54,6 @@ Simulation {
}, },
pc: 2, pc: 2,
memory_write_log: [], memory_write_log: [],
assert_failed_log: [],
memories: StatePart { memories: StatePart {
value: [], value: [],
}, },
@ -78,7 +77,6 @@ Simulation {
.. ..
}, },
}, },
global_io: {},
main_module: SimulationModuleState { main_module: SimulationModuleState {
base_targets: [ base_targets: [
Instance { Instance {
@ -100,9 +98,9 @@ Simulation {
}.o, }.o,
}, },
did_initial_settle: true, did_initial_settle: true,
clocks_for_past: {},
}, },
extern_modules: [], extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule { trace_decls: TraceModule {
name: "connect_const", name: "connect_const",
children: [ children: [
@ -126,20 +124,13 @@ Simulation {
index: StatePartIndex<BigSlots>(0), index: StatePartIndex<BigSlots>(0),
ty: UInt<8>, ty: UInt<8>,
}, },
maybe_changed: true,
state: 0x05, state: 0x05,
last_state: 0x05, last_state: 0x05,
}, },
], ],
trace_memories: {}, trace_memories: {},
trace_writers: [], trace_writers: [],
clocks_triggered: [],
event_queue: EventQueue(EventQueueData {
instant: 0 s, instant: 0 s,
events: {}, clocks_triggered: [],
}),
waiting_sensitivity_sets_by_address: {},
waiting_sensitivity_sets_by_compiled_value: {},
asserts: [],
.. ..
} }

View file

@ -21,7 +21,7 @@ Simulation {
}, },
SlotDebugData { SlotDebugData {
name: "", name: "",
ty: UInt<1>, ty: Bool,
}, },
SlotDebugData { SlotDebugData {
name: "", name: "",
@ -51,12 +51,12 @@ Simulation {
insns: [ insns: [
// at: module-XXXXXXXXXX.rs:1:1 // at: module-XXXXXXXXXX.rs:1:1
0: Const { 0: Const {
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
value: 0x1, value: 0x1,
}, },
1: Copy { 1: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset }, dest: StatePartIndex<BigSlots>(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset },
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
}, },
// at: module-XXXXXXXXXX.rs:4:1 // at: module-XXXXXXXXXX.rs:4:1
2: Copy { 2: Copy {
@ -80,7 +80,6 @@ Simulation {
}, },
pc: 5, pc: 5,
memory_write_log: [], memory_write_log: [],
assert_failed_log: [],
memories: StatePart { memories: StatePart {
value: [], value: [],
}, },
@ -107,7 +106,6 @@ Simulation {
.. ..
}, },
}, },
global_io: {},
main_module: SimulationModuleState { main_module: SimulationModuleState {
base_targets: [ base_targets: [
Instance { Instance {
@ -143,9 +141,9 @@ Simulation {
}.reset_out, }.reset_out,
}, },
did_initial_settle: true, did_initial_settle: true,
clocks_for_past: {},
}, },
extern_modules: [], extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule { trace_decls: TraceModule {
name: "connect_const_reset", name: "connect_const_reset",
children: [ children: [
@ -177,7 +175,6 @@ Simulation {
kind: BigAsyncReset { kind: BigAsyncReset {
index: StatePartIndex<BigSlots>(0), index: StatePartIndex<BigSlots>(0),
}, },
maybe_changed: true,
state: 0x1, state: 0x1,
last_state: 0x1, last_state: 0x1,
}, },
@ -186,7 +183,6 @@ Simulation {
kind: BigBool { kind: BigBool {
index: StatePartIndex<BigSlots>(1), index: StatePartIndex<BigSlots>(1),
}, },
maybe_changed: true,
state: 0x1, state: 0x1,
last_state: 0x1, last_state: 0x1,
}, },
@ -201,13 +197,7 @@ Simulation {
}, },
), ),
], ],
clocks_triggered: [],
event_queue: EventQueue(EventQueueData {
instant: 1 μs, instant: 1 μs,
events: {}, clocks_triggered: [],
}),
waiting_sensitivity_sets_by_address: {},
waiting_sensitivity_sets_by_compiled_value: {},
asserts: [],
.. ..
} }

View file

@ -1,11 +1,11 @@
$timescale 1 ps $end $timescale 1 ps $end
$scope module connect_const_reset $end $scope module connect_const_reset $end
$var wire 1 8ke|= reset_out $end $var wire 1 ! reset_out $end
$var wire 1 {"c@= bit_out $end $var wire 1 " bit_out $end
$upscope $end $upscope $end
$enddefinitions $end $enddefinitions $end
$dumpvars $dumpvars
18ke|= 1!
1{"c@= 1"
$end $end
#1000000 #1000000

Some files were not shown because too many files have changed in this diff Show more