Compare commits

..

No commits in common. "master" and "square-brackets-generics-experiment" have entirely different histories.

111 changed files with 2007 additions and 47928 deletions

View file

@ -1,77 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# See Notices.txt for copyright information
on:
workflow_call:
outputs:
cache-primary-key:
value: ${{ jobs.deps.outputs.cache-primary-key }}
jobs:
deps:
runs-on: debian-12
outputs:
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
steps:
- uses: https://code.forgejo.org/actions/checkout@v3
with:
fetch-depth: 0
- uses: https://code.forgejo.org/actions/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://github.com/YosysHQ/sby.git deps/sby
- name: Build Z3
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
make -C deps/z3/build -j"$(nproc)"
- name: Build Yosys
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys
make -C deps/yosys -j"$(nproc)"
- uses: https://code.forgejo.org/actions/cache/save@v3
if: steps.restore-deps.outputs.cache-hit != 'true'
with:
path: deps
key: ${{ steps.restore-deps.outputs.cache-primary-key }}

View file

@ -1,61 +1,19 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# See Notices.txt for copyright information
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
deps:
uses: ./.forgejo/workflows/deps.yml
test: test:
runs-on: debian-12 runs-on: debian-12
needs: deps
steps: steps:
- uses: https://code.forgejo.org/actions/checkout@v3 - uses: https://code.forgejo.org/actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- run: | - run: |
scripts/check-copyright.sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1
- 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.82.0
source "$HOME/.cargo/env" source "$HOME/.cargo/env"
echo "$PATH" >> "$GITHUB_PATH" echo "$PATH" >> "$GITHUB_PATH"
- uses: https://code.forgejo.org/actions/cache/restore@v3
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://github.com/Swatinem/rust-cache@v2 - uses: https://github.com/Swatinem/rust-cache@v2
with: with:
save-if: ${{ github.ref == 'refs/heads/master' }} save-if: ${{ github.ref == 'refs/heads/master' }}
- run: cargo test - run: cargo test
- run: cargo build --tests --features=unstable-doc - run: cargo test --features=unstable-doc
- run: cargo test --doc --features=unstable-doc
- run: cargo doc --features=unstable-doc - run: cargo doc --features=unstable-doc

4
.gitignore vendored
View file

@ -1,4 +1,2 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# See Notices.txt for copyright information
/target /target
.vscode .vscode

228
Cargo.lock generated
View file

@ -56,7 +56,7 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
dependencies = [ dependencies = [
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]] [[package]]
@ -66,21 +66,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -121,20 +109,6 @@ dependencies = [
"wyz", "wyz",
] ]
[[package]]
name = "blake3"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"serde",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.4" version = "0.10.4"
@ -144,15 +118,6 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "cc"
version = "1.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
dependencies = [
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -205,12 +170,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "constant_time_eq"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.12" version = "0.2.12"
@ -230,27 +189,6 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "ctor"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "derive_destructure2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64b697ac90ff296f0fc031ee5a61c7ac31fb9fff50e3fb32873b09223613fc0c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@ -280,7 +218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]] [[package]]
@ -301,39 +239,32 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]] [[package]]
name = "fayalite" name = "fayalite"
version = "0.3.0" version = "0.2.0"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"blake3",
"clap", "clap",
"ctor",
"eyre", "eyre",
"fayalite-proc-macros", "fayalite-proc-macros",
"fayalite-visit-gen", "fayalite-visit-gen",
"hashbrown", "hashbrown",
"jobslot",
"num-bigint", "num-bigint",
"num-traits", "num-traits",
"os_pipe",
"petgraph",
"serde", "serde",
"serde_json", "serde_json",
"tempfile",
"trybuild", "trybuild",
"vec_map",
"which", "which",
] ]
[[package]] [[package]]
name = "fayalite-proc-macros" name = "fayalite-proc-macros"
version = "0.3.0" version = "0.2.0"
dependencies = [ dependencies = [
"fayalite-proc-macros-impl", "fayalite-proc-macros-impl",
] ]
[[package]] [[package]]
name = "fayalite-proc-macros-impl" name = "fayalite-proc-macros-impl"
version = "0.3.0" version = "0.2.0"
dependencies = [ dependencies = [
"base16ct", "base16ct",
"num-bigint", "num-bigint",
@ -347,7 +278,7 @@ dependencies = [
[[package]] [[package]]
name = "fayalite-visit-gen" name = "fayalite-visit-gen"
version = "0.3.0" version = "0.2.0"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"prettyplease", "prettyplease",
@ -359,12 +290,6 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]] [[package]]
name = "funty" name = "funty"
version = "2.0.0" version = "2.0.0"
@ -381,17 +306,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "getrandom"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.1" version = "0.3.1"
@ -420,7 +334,7 @@ version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [ dependencies = [
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]] [[package]]
@ -431,9 +345,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.5.0" version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -452,20 +366,6 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jobslot"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d"
dependencies = [
"cfg-if",
"derive_destructure2",
"getrandom",
"libc",
"scopeguard",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.153" version = "0.2.153"
@ -480,10 +380,11 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.6" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [ dependencies = [
"autocfg",
"num-integer", "num-integer",
"num-traits", "num-traits",
] ]
@ -512,25 +413,6 @@ 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 = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "os_pipe"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "petgraph"
version = "0.6.5"
source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.20" version = "0.2.20"
@ -543,9 +425,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.92" version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -575,7 +457,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]] [[package]]
@ -584,12 +466,6 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.202" version = "1.0.202"
@ -633,12 +509,6 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -647,9 +517,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.93" version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -671,7 +541,7 @@ dependencies = [
"cfg-if", "cfg-if",
"fastrand", "fastrand",
"rustix", "rustix",
"windows-sys 0.52.0", "windows-sys",
] ]
[[package]] [[package]]
@ -736,24 +606,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "which" name = "which"
version = "6.0.1" version = "6.0.1"
@ -806,25 +664,15 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm",
"windows_aarch64_msvc", "windows_aarch64_msvc",
"windows_i686_gnu", "windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc", "windows_i686_msvc",
"windows_x86_64_gnu", "windows_x86_64_gnu",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm",
@ -833,51 +681,45 @@ dependencies = [
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.6" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.6" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.6" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.6" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.6" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.6" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]] [[package]]
name = "winsafe" name = "winsafe"

View file

@ -5,41 +5,31 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.3.0" version = "0.2.0"
license = "LGPL-3.0-or-later" license = "LGPL-3.0-or-later"
edition = "2021" edition = "2021"
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.82.0" rust-version = "1.80.1"
[workspace.dependencies] [workspace.dependencies]
fayalite-proc-macros = { version = "=0.3.0", path = "crates/fayalite-proc-macros" } fayalite-proc-macros = { version = "=0.2.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.2.0", path = "crates/fayalite-proc-macros-impl" }
fayalite-visit-gen = { version = "=0.3.0", path = "crates/fayalite-visit-gen" } fayalite-visit-gen = { version = "=0.2.0", path = "crates/fayalite-visit-gen" }
base16ct = "0.2.0" base16ct = "0.2.0"
bitvec = { version = "1.0.1", features = ["serde"] } bitvec = { version = "1.0.1", features = ["serde"] }
blake3 = { version = "1.5.4", features = ["serde"] }
clap = { version = "4.5.9", features = ["derive", "env", "string"] }
ctor = "0.2.8"
eyre = "0.6.12"
hashbrown = "0.14.3" hashbrown = "0.14.3"
indexmap = { version = "2.5.0", features = ["serde"] } indexmap = { version = "2.2.6", features = ["serde"] }
jobslot = "0.2.19" num-bigint = "0.4.4"
num-bigint = "0.4.6"
num-traits = "0.2.16" num-traits = "0.2.16"
os_pipe = "1.2.1"
# TODO: switch back to crates.io once petgraph accepts PR #684 and releases a new version
petgraph = { git = "https://github.com/programmerjake/petgraph.git", rev = "258ea8071209a924b73fe96f9f87a3b7b45cbc9f" }
prettyplease = "0.2.20" prettyplease = "0.2.20"
proc-macro2 = "1.0.83" proc-macro2 = "1.0.83"
quote = "1.0.36" quote = "1.0.36"
serde = { version = "1.0.202", features = ["derive"] } serde = { version = "1.0.202", features = ["derive"] }
serde_json = { version = "1.0.117", features = ["preserve_order"] } serde_json = { version = "1.0.117", features = ["preserve_order"] }
sha2 = "0.10.8" sha2 = "0.10.8"
syn = { version = "2.0.93", features = ["full", "fold", "visit", "extra-traits"] } syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"] }
tempfile = "3.10.1" tempfile = "3.10.1"
thiserror = "1.0.61" thiserror = "1.0.61"
trybuild = "1.0" trybuild = "1.0"
vec_map = "0.8.2"
which = "6.0.1"

View file

@ -1,7 +1,3 @@
<!--
SPDX-License-Identifier: LGPL-3.0-or-later
See Notices.txt for copyright information
-->
# Fayalite # Fayalite
Fayalite is a library for designing digital hardware -- a hardware description language (HDL) embedded in the Rust programming language. Fayalite's semantics are based on [FIRRTL] as interpreted by [LLVM CIRCT](https://circt.llvm.org/docs/Dialects/FIRRTL/FIRRTLAnnotations/). Fayalite is a library for designing digital hardware -- a hardware description language (HDL) embedded in the Rust programming language. Fayalite's semantics are based on [FIRRTL] as interpreted by [LLVM CIRCT](https://circt.llvm.org/docs/Dialects/FIRRTL/FIRRTLAnnotations/).

View file

@ -13,11 +13,11 @@ rust-version.workspace = true
version.workspace = true version.workspace = true
[dependencies] [dependencies]
base16ct.workspace = true base16ct = { workspace = true }
num-bigint.workspace = true num-bigint = { workspace = true }
prettyplease.workspace = true prettyplease = { workspace = true }
proc-macro2.workspace = true proc-macro2 = { workspace = true }
quote.workspace = true quote = { workspace = true }
sha2.workspace = true sha2 = { workspace = true }
syn.workspace = true syn = { workspace = true }
tempfile.workspace = true tempfile = { workspace = true }

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{ use crate::{
hdl_type_common::{ hdl_type_common::{
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField,
@ -21,13 +19,13 @@ use syn::{
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ParsedBundle { pub(crate) struct ParsedBundle {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ItemOptions, kw::hdl>, pub(crate) options: HdlAttr<ItemOptions>,
pub(crate) vis: Visibility, pub(crate) vis: Visibility,
pub(crate) struct_token: Token![struct], pub(crate) struct_token: Token![struct],
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>, pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
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>>>,
pub(crate) mask_type_ident: Ident, pub(crate) mask_type_ident: Ident,
pub(crate) mask_type_match_variant_ident: Ident, pub(crate) mask_type_match_variant_ident: Ident,
pub(crate) match_variant_ident: Ident, pub(crate) match_variant_ident: Ident,
@ -40,7 +38,7 @@ impl ParsedBundle {
errors: &mut Errors, errors: &mut Errors,
field: &mut Field, field: &mut Field,
index: usize, index: usize,
) -> Option<HdlAttr<kw::flip, kw::hdl>> { ) -> Option<HdlAttr<kw::flip>> {
let Field { let Field {
attrs, attrs,
vis: _, vis: _,
@ -58,7 +56,8 @@ impl ParsedBundle {
} }
*mutability = FieldMutability::None; *mutability = FieldMutability::None;
colon_token.get_or_insert(Token![:](ident.span())); colon_token.get_or_insert(Token![:](ident.span()));
errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs)) let options = errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs));
options
} }
fn parse(item: ItemStruct) -> syn::Result<Self> { fn parse(item: ItemStruct) -> syn::Result<Self> {
let ItemStruct { let ItemStruct {
@ -72,9 +71,7 @@ impl ParsedBundle {
} = item; } = item;
let mut errors = Errors::new(); let mut errors = Errors::new();
let mut options = errors let mut options = errors
.unwrap_or_default(HdlAttr::<ItemOptions, kw::hdl>::parse_and_take_attr( .unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs))
&mut attrs,
))
.unwrap_or_default(); .unwrap_or_default();
errors.ok(options.body.validate()); errors.ok(options.body.validate());
let ItemOptions { let ItemOptions {
@ -83,7 +80,6 @@ impl ParsedBundle {
custom_bounds, custom_bounds,
no_static: _, no_static: _,
no_runtime_generics: _, no_runtime_generics: _,
cmp_eq: _,
} = options.body; } = options.body;
let mut fields = match fields { let mut fields = match fields {
syn::Fields::Named(fields) => fields, syn::Fields::Named(fields) => fields,
@ -341,7 +337,7 @@ impl ToTokens for Builder {
})); }));
quote_spanned! {self.ident.span()=> quote_spanned! {self.ident.span()=>
#[automatically_derived] #[automatically_derived]
#[allow(non_camel_case_types, non_snake_case, dead_code)] #[allow(non_camel_case_types, dead_code)]
impl #impl_generics #unfilled_ty impl #impl_generics #unfilled_ty
#where_clause #where_clause
{ {
@ -431,18 +427,16 @@ impl ToTokens for ParsedBundle {
builder_ident, builder_ident,
mask_type_builder_ident, mask_type_builder_ident,
} = self; } = self;
let span = ident.span();
let ItemOptions { let ItemOptions {
outline_generated: _, outline_generated: _,
target, target,
custom_bounds: _, custom_bounds: _,
no_static, no_static,
no_runtime_generics, no_runtime_generics,
cmp_eq,
} = &options.body; } = &options.body;
let target = get_target(target, ident); let target = get_target(target, ident);
let mut item_attrs = attrs.clone(); let mut item_attrs = attrs.clone();
item_attrs.push(common_derives(span)); item_attrs.push(common_derives(ident.span()));
ItemStruct { ItemStruct {
attrs: item_attrs, attrs: item_attrs,
vis: vis.clone(), vis: vis.clone(),
@ -464,19 +458,19 @@ impl ToTokens for ParsedBundle {
.map(|ParsedField { ident, ty, .. }| { .map(|ParsedField { ident, ty, .. }| {
let ident = ident.as_ref().unwrap(); let ident = ident.as_ref().unwrap();
let expr = ty.make_hdl_type_expr(context); let expr = ty.make_hdl_type_expr(context);
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: #expr, #ident: #expr,
} }
}) })
.collect(); .collect();
parse_quote_spanned! {span=> parse_quote_spanned! {ident.span()=>
#target { #target {
#(#fields)* #(#fields)*
} }
} }
}) })
} }
let mut wrapped_in_const = WrappedInConst::new(tokens, span); let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
let tokens = wrapped_in_const.inner(); let tokens = wrapped_in_const.inner();
let builder = Builder { let builder = Builder {
vis: vis.clone(), vis: vis.clone(),
@ -490,8 +484,9 @@ impl ToTokens for ParsedBundle {
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 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 { ident, ty, .. } in &mut mask_type_fields.named {
*ty = parse_quote_spanned! {span=> let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
<#ty as ::fayalite::ty::Type>::MaskType <#ty as ::fayalite::ty::Type>::MaskType
}; };
} }
@ -510,8 +505,8 @@ impl ToTokens for ParsedBundle {
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled); mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled);
ItemStruct { ItemStruct {
attrs: vec![ attrs: vec![
common_derives(span), common_derives(ident.span()),
parse_quote_spanned! {span=> parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types, dead_code)] #[allow(non_camel_case_types, dead_code)]
}, },
], ],
@ -524,15 +519,16 @@ impl ToTokens for ParsedBundle {
} }
.to_tokens(tokens); .to_tokens(tokens);
let mut mask_type_match_variant_fields = mask_type_fields; let mut mask_type_match_variant_fields = mask_type_fields;
for Field { ty, .. } in &mut mask_type_match_variant_fields.named { for Field { ident, ty, .. } in &mut mask_type_match_variant_fields.named {
*ty = parse_quote_spanned! {span=> let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
::fayalite::expr::Expr<#ty> ::fayalite::expr::Expr<#ty>
}; };
} }
ItemStruct { ItemStruct {
attrs: vec![ attrs: vec![
common_derives(span), common_derives(ident.span()),
parse_quote_spanned! {span=> parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types, dead_code)] #[allow(non_camel_case_types, dead_code)]
}, },
], ],
@ -545,15 +541,16 @@ impl ToTokens for ParsedBundle {
} }
.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 { ident, ty, .. } in &mut match_variant_fields.named {
*ty = parse_quote_spanned! {span=> let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
::fayalite::expr::Expr<#ty> ::fayalite::expr::Expr<#ty>
}; };
} }
ItemStruct { ItemStruct {
attrs: vec![ attrs: vec![
common_derives(span), common_derives(ident.span()),
parse_quote_spanned! {span=> parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types, dead_code)] #[allow(non_camel_case_types, dead_code)]
}, },
], ],
@ -565,20 +562,17 @@ impl ToTokens for ParsedBundle {
semi_token: None, semi_token: None,
} }
.to_tokens(tokens); .to_tokens(tokens);
let this_token = Ident::new("__this", span);
let fields_token = Ident::new("__fields", span);
let self_token = Token![self](span);
let match_variant_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let match_variant_body_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();
let ident_str = ident.to_string(); let ident_str = ident.to_string();
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: ::fayalite::expr::Expr::field(#this_token, #ident_str), #ident: ::fayalite::expr::Expr::field(__this, #ident_str),
} }
})); }));
let mask_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let mask_type_body_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! {ident.span()=>
#ident: ::fayalite::ty::Type::mask_type(&#self_token.#ident), #ident: ::fayalite::ty::Type::mask_type(&self.#ident),
} }
})); }));
let from_canonical_body_fields = let from_canonical_body_fields =
@ -586,16 +580,16 @@ impl ToTokens for ParsedBundle {
|((index, field), flip)| { |((index, field), flip)| {
let ident: &Ident = field.ident().as_ref().unwrap(); let ident: &Ident = field.ident().as_ref().unwrap();
let ident_str = ident.to_string(); let ident_str = ident.to_string();
let not_flipped = flip.is_none().then(|| Token![!](span)); let flipped = flip.is_some();
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: { #ident: {
let ::fayalite::bundle::BundleField { let ::fayalite::bundle::BundleField {
name: __name, name: __name,
flipped: __flipped, flipped: __flipped,
ty: __ty, ty: __ty,
} = #fields_token[#index]; } = __fields[#index];
::fayalite::__std::assert_eq!(&*__name, #ident_str); ::fayalite::__std::assert_eq!(&*__name, #ident_str);
::fayalite::__std::assert!(#not_flipped __flipped); ::fayalite::__std::assert_eq!(__flipped, #flipped);
::fayalite::ty::Type::from_canonical(__ty) ::fayalite::ty::Type::from_canonical(__ty)
}, },
} }
@ -606,17 +600,17 @@ impl ToTokens for ParsedBundle {
let ident: &Ident = field.ident().as_ref().unwrap(); let ident: &Ident = field.ident().as_ref().unwrap();
let ident_str = ident.to_string(); let ident_str = ident.to_string();
let flipped = flip.is_some(); let flipped = flip.is_some();
quote_spanned! {span=> quote_spanned! {ident.span()=>
::fayalite::bundle::BundleField { ::fayalite::bundle::BundleField {
name: ::fayalite::intern::Intern::intern(#ident_str), name: ::fayalite::intern::Intern::intern(#ident_str),
flipped: #flipped, flipped: #flipped,
ty: ::fayalite::ty::Type::canonical(&#self_token.#ident), ty: ::fayalite::ty::Type::canonical(&self.#ident),
}, },
} }
}, },
)); ));
let fields_len = fields.named().into_iter().len(); let fields_len = fields.named().into_iter().len();
quote_spanned! {span=> quote_spanned! {ident.span()=>
#[automatically_derived] #[automatically_derived]
impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics
#where_clause #where_clause
@ -632,7 +626,7 @@ impl ToTokens for ParsedBundle {
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>; >;
fn match_variants( fn match_variants(
#this_token: ::fayalite::expr::Expr<Self>, __this: ::fayalite::expr::Expr<Self>,
__source_location: ::fayalite::source_location::SourceLocation, __source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
let __retval = #mask_type_match_variant_ident { let __retval = #mask_type_match_variant_ident {
@ -640,19 +634,19 @@ impl ToTokens for ParsedBundle {
}; };
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
} }
fn mask_type(&#self_token) -> <Self as ::fayalite::ty::Type>::MaskType { fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
*#self_token *self
} }
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType { fn canonical(&self) -> ::fayalite::ty::CanonicalType {
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(#self_token))) ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self)))
} }
#[track_caller] #[track_caller]
fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self {
let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else {
::fayalite::__std::panic!("expected bundle"); ::fayalite::__std::panic!("expected bundle");
}; };
let #fields_token = ::fayalite::bundle::BundleType::fields(&__bundle); let __fields = ::fayalite::bundle::BundleType::fields(&__bundle);
::fayalite::__std::assert_eq!(#fields_token.len(), #fields_len, "bundle has wrong number of fields"); ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields");
Self { Self {
#(#from_canonical_body_fields)* #(#from_canonical_body_fields)*
} }
@ -667,7 +661,7 @@ impl ToTokens for ParsedBundle {
{ {
type Builder = #unfilled_mask_type_builder_ty; type Builder = #unfilled_mask_type_builder_ty;
type FilledBuilder = #filled_mask_type_builder_ty; type FilledBuilder = #filled_mask_type_builder_ty;
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
} }
} }
@ -682,12 +676,12 @@ impl ToTokens for ParsedBundle {
impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics
#where_clause #where_clause
{ {
fn expr_deref(#this_token: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant { fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
let #this_token = *#this_token; let __this = *__this;
let __retval = #mask_type_match_variant_ident { let __retval = #mask_type_match_variant_ident {
#(#match_variant_body_fields)* #(#match_variant_body_fields)*
}; };
::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
} }
} }
#[automatically_derived] #[automatically_derived]
@ -705,7 +699,7 @@ impl ToTokens for ParsedBundle {
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>; >;
fn match_variants( fn match_variants(
#this_token: ::fayalite::expr::Expr<Self>, __this: ::fayalite::expr::Expr<Self>,
__source_location: ::fayalite::source_location::SourceLocation, __source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
let __retval = #match_variant_ident { let __retval = #match_variant_ident {
@ -713,21 +707,21 @@ impl ToTokens for ParsedBundle {
}; };
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
} }
fn mask_type(&#self_token) -> <Self as ::fayalite::ty::Type>::MaskType { fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
#mask_type_ident { #mask_type_ident {
#(#mask_type_body_fields)* #(#mask_type_body_fields)*
} }
} }
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType { fn canonical(&self) -> ::fayalite::ty::CanonicalType {
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(#self_token))) ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self)))
} }
#[track_caller] #[track_caller]
fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self {
let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else {
::fayalite::__std::panic!("expected bundle"); ::fayalite::__std::panic!("expected bundle");
}; };
let #fields_token = ::fayalite::bundle::BundleType::fields(&__bundle); let __fields = ::fayalite::bundle::BundleType::fields(&__bundle);
::fayalite::__std::assert_eq!(#fields_token.len(), #fields_len, "bundle has wrong number of fields"); ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields");
Self { Self {
#(#from_canonical_body_fields)* #(#from_canonical_body_fields)*
} }
@ -742,7 +736,7 @@ impl ToTokens for ParsedBundle {
{ {
type Builder = #unfilled_builder_ty; type Builder = #unfilled_builder_ty;
type FilledBuilder = #filled_builder_ty; type FilledBuilder = #filled_builder_ty;
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
} }
} }
@ -757,79 +751,16 @@ impl ToTokens for ParsedBundle {
impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics
#where_clause #where_clause
{ {
fn expr_deref(#this_token: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant { fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
let #this_token = *#this_token; let __this = *__this;
let __retval = #match_variant_ident { let __retval = #match_variant_ident {
#(#match_variant_body_fields)* #(#match_variant_body_fields)*
}; };
::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
} }
} }
} }
.to_tokens(tokens); .to_tokens(tokens);
if let Some((cmp_eq,)) = cmp_eq {
let mut where_clause =
Generics::from(generics)
.where_clause
.unwrap_or_else(|| syn::WhereClause {
where_token: Token![where](span),
predicates: Punctuated::new(),
});
let mut fields_cmp_eq = vec![];
let mut fields_cmp_ne = vec![];
for field in fields.named() {
let field_ident = field.ident();
let field_ty = field.ty();
where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
});
fields_cmp_eq.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
});
fields_cmp_ne.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
});
}
let cmp_eq_body;
let cmp_ne_body;
if fields_len == 0 {
cmp_eq_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&true)
};
cmp_ne_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&false)
};
} else {
cmp_eq_body = quote_spanned! {span=>
#(#fields_cmp_eq)&*
};
cmp_ne_body = quote_spanned! {span=>
#(#fields_cmp_ne)|*
};
};
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
#where_clause
{
fn cmp_eq(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
#cmp_eq_body
}
fn cmp_ne(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
#cmp_ne_body
}
}
}
.to_tokens(tokens);
}
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
let static_generics = generics.clone().for_static_type(); let static_generics = generics.clone().for_static_type();
let (static_impl_generics, static_type_generics, static_where_clause) = let (static_impl_generics, static_type_generics, static_where_clause) =
@ -837,7 +768,7 @@ impl ToTokens for ParsedBundle {
let static_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let static_type_body_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();
let ty = field.ty(); let ty = field.ty();
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: <#ty as ::fayalite::ty::StaticType>::TYPE, #ident: <#ty as ::fayalite::ty::StaticType>::TYPE,
} }
})); }));
@ -845,26 +776,28 @@ impl ToTokens for ParsedBundle {
Vec::from_iter(fields.named().into_iter().map(|field| { 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();
let ty = field.ty(); let ty = field.ty();
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE, #ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE,
} }
})); }));
let type_properties = format_ident!("__type_properties", span = span); let type_properties = format_ident!("__type_properties", span = ident.span());
let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| { let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
let ident: &Ident = field.ident().as_ref().unwrap();
let flipped = field_flip.is_some(); let flipped = field_flip.is_some();
let ty = field.ty(); let ty = field.ty();
quote_spanned! {span=> quote_spanned! {ident.span()=>
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES); let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES);
} }
})); }));
let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| { let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
let ident: &Ident = field.ident().as_ref().unwrap();
let flipped = field_flip.is_some(); let flipped = field_flip.is_some();
let ty = field.ty(); let ty = field.ty();
quote_spanned! {span=> quote_spanned! {ident.span()=>
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES); let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES);
} }
})); }));
quote_spanned! {span=> quote_spanned! {ident.span()=>
#[automatically_derived] #[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics
#static_where_clause #static_where_clause

View file

@ -1,11 +1,9 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{ use crate::{
hdl_type_common::{ hdl_type_common::{
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics,
ParsedType, SplitForImpl, TypesParser, WrappedInConst, ParsedType, SplitForImpl, TypesParser, WrappedInConst,
}, },
kw, Errors, HdlAttr, PairsIterExt, Errors, HdlAttr, PairsIterExt,
}; };
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{format_ident, quote_spanned, ToTokens}; use quote::{format_ident, quote_spanned, ToTokens};
@ -31,7 +29,7 @@ crate::options! {
pub(crate) struct ParsedVariantField { pub(crate) struct ParsedVariantField {
pub(crate) paren_token: Paren, pub(crate) paren_token: Paren,
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<FieldOptions, kw::hdl>, pub(crate) options: HdlAttr<FieldOptions>,
pub(crate) ty: MaybeParsed<ParsedType, Type>, pub(crate) ty: MaybeParsed<ParsedType, Type>,
pub(crate) comma_token: Option<Token![,]>, pub(crate) comma_token: Option<Token![,]>,
} }
@ -39,7 +37,7 @@ pub(crate) struct ParsedVariantField {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ParsedVariant { pub(crate) struct ParsedVariant {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<VariantOptions, kw::hdl>, pub(crate) options: HdlAttr<VariantOptions>,
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) field: Option<ParsedVariantField>, pub(crate) field: Option<ParsedVariantField>,
} }
@ -121,7 +119,7 @@ impl ParsedVariant {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ParsedEnum { pub(crate) struct ParsedEnum {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ItemOptions, kw::hdl>, pub(crate) options: HdlAttr<ItemOptions>,
pub(crate) vis: Visibility, pub(crate) vis: Visibility,
pub(crate) enum_token: Token![enum], pub(crate) enum_token: Token![enum],
pub(crate) ident: Ident, pub(crate) ident: Ident,
@ -144,9 +142,7 @@ impl ParsedEnum {
} = item; } = item;
let mut errors = Errors::new(); let mut errors = Errors::new();
let mut options = errors let mut options = errors
.unwrap_or_default(HdlAttr::<ItemOptions, kw::hdl>::parse_and_take_attr( .unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs))
&mut attrs,
))
.unwrap_or_default(); .unwrap_or_default();
errors.ok(options.body.validate()); errors.ok(options.body.validate());
let ItemOptions { let ItemOptions {
@ -155,11 +151,7 @@ impl ParsedEnum {
custom_bounds, custom_bounds,
no_static: _, no_static: _,
no_runtime_generics: _, no_runtime_generics: _,
cmp_eq,
} = options.body; } = options.body;
if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
}
attrs.retain(|attr| { attrs.retain(|attr| {
if attr.path().is_ident("repr") { if attr.path().is_ident("repr") {
errors.error(attr, "#[repr] is not supported on #[hdl] enums"); errors.error(attr, "#[repr] is not supported on #[hdl] enums");
@ -208,19 +200,17 @@ impl ToTokens for ParsedEnum {
variants, variants,
match_variant_ident, match_variant_ident,
} = self; } = self;
let span = ident.span();
let ItemOptions { let ItemOptions {
outline_generated: _, outline_generated: _,
target, target,
custom_bounds: _, custom_bounds: _,
no_static, no_static,
no_runtime_generics, no_runtime_generics,
cmp_eq: _, // TODO: implement cmp_eq for enums
} = &options.body; } = &options.body;
let target = get_target(target, ident); let target = get_target(target, ident);
let mut struct_attrs = attrs.clone(); let mut struct_attrs = attrs.clone();
struct_attrs.push(common_derives(span)); struct_attrs.push(common_derives(ident.span()));
struct_attrs.push(parse_quote_spanned! {span=> struct_attrs.push(parse_quote_spanned! {ident.span()=>
#[allow(non_snake_case)] #[allow(non_snake_case)]
}); });
let struct_fields = Punctuated::from_iter(variants.pairs().map_pair_value_ref( let struct_fields = Punctuated::from_iter(variants.pairs().map_pair_value_ref(
@ -244,8 +234,8 @@ impl ToTokens for ParsedEnum {
colon_token = Token![:](paren_token.span.open()); colon_token = Token![:](paren_token.span.open());
ty.clone().into() ty.clone().into()
} else { } else {
colon_token = Token![:](span); colon_token = Token![:](ident.span());
parse_quote_spanned! {span=> parse_quote_spanned! {ident.span()=>
() ()
} }
}; };
@ -288,30 +278,30 @@ impl ToTokens for ParsedEnum {
}) = field }) = field
{ {
let expr = ty.make_hdl_type_expr(context); let expr = ty.make_hdl_type_expr(context);
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: #expr, #ident: #expr,
} }
} else { } else {
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: (), #ident: (),
} }
} }
}) })
.collect(); .collect();
parse_quote_spanned! {span=> parse_quote_spanned! {ident.span()=>
#target { #target {
#(#fields)* #(#fields)*
} }
} }
}) })
} }
let mut wrapped_in_const = WrappedInConst::new(tokens, span); let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
let tokens = wrapped_in_const.inner(); let tokens = wrapped_in_const.inner();
{ {
let mut wrapped_in_const = WrappedInConst::new(tokens, span); let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
let tokens = wrapped_in_const.inner(); let tokens = wrapped_in_const.inner();
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! {ident.span()=>
#[allow(dead_code)] #[allow(dead_code)]
}); });
ItemEnum { ItemEnum {
@ -360,7 +350,7 @@ impl ToTokens for ParsedEnum {
.to_tokens(tokens); .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! {ident.span()=>
#[allow(dead_code, non_camel_case_types)] #[allow(dead_code, non_camel_case_types)]
}); });
ItemEnum { ItemEnum {
@ -395,7 +385,7 @@ impl ToTokens for ParsedEnum {
mutability: FieldMutability::None, mutability: FieldMutability::None,
ident: None, ident: None,
colon_token: None, colon_token: None,
ty: parse_quote_spanned! {span=> ty: parse_quote_spanned! {ident.span()=>
::fayalite::expr::Expr<#ty> ::fayalite::expr::Expr<#ty>
}, },
}, },
@ -409,22 +399,21 @@ impl ToTokens for ParsedEnum {
)), )),
} }
.to_tokens(tokens); .to_tokens(tokens);
let self_token = Token![self](span);
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! {ident.span()=>
#[automatically_derived] #[automatically_derived]
impl #impl_generics #target #type_generics impl #impl_generics #target #type_generics
#where_clause #where_clause
{ {
#[allow(non_snake_case, dead_code)] #[allow(non_snake_case, dead_code)]
#vis fn #ident<__V: ::fayalite::expr::ToExpr<Type = #ty>>( #vis fn #ident<__V: ::fayalite::expr::ToExpr<Type = #ty>>(
#self_token, self,
v: __V, v: __V,
) -> ::fayalite::expr::Expr<Self> { ) -> ::fayalite::expr::Expr<Self> {
::fayalite::expr::ToExpr::to_expr( ::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::EnumLiteral::new_by_index( &::fayalite::expr::ops::EnumLiteral::new_by_index(
#self_token, self,
#index, #index,
::fayalite::__std::option::Option::Some( ::fayalite::__std::option::Option::Some(
::fayalite::expr::Expr::canonical( ::fayalite::expr::Expr::canonical(
@ -437,16 +426,16 @@ impl ToTokens for ParsedEnum {
} }
} }
} else { } else {
quote_spanned! {span=> quote_spanned! {ident.span()=>
#[automatically_derived] #[automatically_derived]
impl #impl_generics #target #type_generics impl #impl_generics #target #type_generics
#where_clause #where_clause
{ {
#[allow(non_snake_case, dead_code)] #[allow(non_snake_case, dead_code)]
#vis fn #ident(#self_token) -> ::fayalite::expr::Expr<Self> { #vis fn #ident(self) -> ::fayalite::expr::Expr<Self> {
::fayalite::expr::ToExpr::to_expr( ::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::EnumLiteral::new_by_index( &::fayalite::expr::ops::EnumLiteral::new_by_index(
#self_token, self,
#index, #index,
::fayalite::__std::option::Option::None, ::fayalite::__std::option::Option::None,
), ),
@ -457,48 +446,46 @@ impl ToTokens for ParsedEnum {
} }
.to_tokens(tokens); .to_tokens(tokens);
} }
let variants_token = Ident::new("variants", span);
let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map( let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map(
|(index, ParsedVariant { ident, field, .. })| { |(index, ParsedVariant { ident, field, .. })| {
let ident_str = ident.to_string(); let ident_str = ident.to_string();
let val = if field.is_some() { let val = if field.is_some() {
let missing_value_msg = format!("expected variant {ident} to have a field"); let missing_value_msg = format!("expected variant {ident} to have a field");
quote_spanned! {span=> quote_spanned! {ident.span()=>
::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg)) ::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg))
} }
} else { } else {
quote_spanned! {span=> quote_spanned! {ident.span()=>
::fayalite::__std::assert!(ty.is_none()); ::fayalite::__std::assert!(ty.is_none());
} }
}; };
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: { #ident: {
let ::fayalite::enum_::EnumVariant { let ::fayalite::enum_::EnumVariant {
name, name,
ty, ty,
} = #variants_token[#index]; } = variants[#index];
::fayalite::__std::assert_eq!(&*name, #ident_str); ::fayalite::__std::assert_eq!(&*name, #ident_str);
#val #val
}, },
} }
}, },
)); ));
let variant_access_token = Ident::new("variant_access", span);
let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map( let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map(
|(index, ParsedVariant { ident, field, .. })| { |(index, ParsedVariant { ident, field, .. })| {
if field.is_some() { if field.is_some() {
quote_spanned! {span=> quote_spanned! {ident.span()=>
#index => #match_variant_ident::#ident( #index => #match_variant_ident::#ident(
::fayalite::expr::ToExpr::to_expr( ::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::VariantAccess::new_by_index( &::fayalite::expr::ops::VariantAccess::new_by_index(
#variant_access_token.base(), variant_access.base(),
#variant_access_token.variant_index(), variant_access.variant_index(),
), ),
), ),
), ),
} }
} else { } else {
quote_spanned! {span=> quote_spanned! {ident.span()=>
#index => #match_variant_ident::#ident, #index => #match_variant_ident::#ident,
} }
} }
@ -516,16 +503,16 @@ impl ToTokens for ParsedEnum {
match field { match field {
Some(ParsedVariantField { options, .. }) => { Some(ParsedVariantField { options, .. }) => {
let FieldOptions {} = options.body; let FieldOptions {} = options.body;
quote_spanned! {span=> quote_spanned! {ident.span()=>
::fayalite::enum_::EnumVariant { ::fayalite::enum_::EnumVariant {
name: ::fayalite::intern::Intern::intern(#ident_str), name: ::fayalite::intern::Intern::intern(#ident_str),
ty: ::fayalite::__std::option::Option::Some( ty: ::fayalite::__std::option::Option::Some(
::fayalite::ty::Type::canonical(&#self_token.#ident), ::fayalite::ty::Type::canonical(&self.#ident),
), ),
}, },
} }
} }
None => quote_spanned! {span=> None => quote_spanned! {ident.span()=>
::fayalite::enum_::EnumVariant { ::fayalite::enum_::EnumVariant {
name: ::fayalite::intern::Intern::intern(#ident_str), name: ::fayalite::intern::Intern::intern(#ident_str),
ty: ::fayalite::__std::option::Option::None, ty: ::fayalite::__std::option::Option::None,
@ -535,7 +522,7 @@ impl ToTokens for ParsedEnum {
}, },
)); ));
let variants_len = variants.len(); let variants_len = variants.len();
quote_spanned! {span=> quote_spanned! {ident.span()=>
#[automatically_derived] #[automatically_derived]
impl #impl_generics ::fayalite::ty::Type for #target #type_generics impl #impl_generics ::fayalite::ty::Type for #target #type_generics
#where_clause #where_clause
@ -553,11 +540,11 @@ impl ToTokens for ParsedEnum {
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
::fayalite::module::enum_match_variants_helper(this, source_location) ::fayalite::module::enum_match_variants_helper(this, source_location)
} }
fn mask_type(&#self_token) -> <Self as ::fayalite::ty::Type>::MaskType { fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
::fayalite::int::Bool ::fayalite::int::Bool
} }
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType { fn canonical(&self) -> ::fayalite::ty::CanonicalType {
::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(#self_token))) ::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(self)))
} }
#[track_caller] #[track_caller]
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -565,8 +552,8 @@ impl ToTokens for ParsedEnum {
let ::fayalite::ty::CanonicalType::Enum(enum_) = canonical_type else { let ::fayalite::ty::CanonicalType::Enum(enum_) = canonical_type else {
::fayalite::__std::panic!("expected enum"); ::fayalite::__std::panic!("expected enum");
}; };
let #variants_token = ::fayalite::enum_::EnumType::variants(&enum_); let variants = ::fayalite::enum_::EnumType::variants(&enum_);
::fayalite::__std::assert_eq!(#variants_token.len(), #variants_len, "enum has wrong number of variants"); ::fayalite::__std::assert_eq!(variants.len(), #variants_len, "enum has wrong number of variants");
Self { Self {
#(#from_canonical_body_fields)* #(#from_canonical_body_fields)*
} }
@ -582,16 +569,16 @@ impl ToTokens for ParsedEnum {
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, scope) = v.activate();
( (
match #variant_access_token.variant_index() { match variant_access.variant_index() {
#(#match_active_scope_match_arms)* #(#match_active_scope_match_arms)*
#variants_len.. => ::fayalite::__std::panic!("invalid variant index"), #variants_len.. => ::fayalite::__std::panic!("invalid variant index"),
}, },
scope, scope,
) )
} }
fn variants(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> { fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> {
::fayalite::intern::Intern::intern(&[ ::fayalite::intern::Intern::intern(&[
#(#variants_body_variants)* #(#variants_body_variants)*
][..]) ][..])
@ -605,35 +592,35 @@ impl ToTokens for ParsedEnum {
static_generics.split_for_impl(); static_generics.split_for_impl();
let static_type_body_variants = let static_type_body_variants =
Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| { Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| {
if field.is_some() { if let Some(_) = field {
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: ::fayalite::ty::StaticType::TYPE, #ident: ::fayalite::ty::StaticType::TYPE,
} }
} else { } else {
quote_spanned! {span=> quote_spanned! {ident.span()=>
#ident: (), #ident: (),
} }
} }
})); }));
let type_properties = format_ident!("__type_properties", span = span); let type_properties = format_ident!("__type_properties", span = ident.span());
let type_properties_variants = let type_properties_variants =
Vec::from_iter(variants.iter().map(|ParsedVariant { field, .. }| { Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| {
let variant = if let Some(ParsedVariantField { ty, .. }) = field { let variant = if let Some(ParsedVariantField { ty, .. }) = field {
quote_spanned! {span=> quote_spanned! {ident.span()=>
::fayalite::__std::option::Option::Some( ::fayalite::__std::option::Option::Some(
<#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES,
) )
} }
} else { } else {
quote_spanned! {span=> quote_spanned! {ident.span()=>
::fayalite::__std::option::Option::None ::fayalite::__std::option::Option::None
} }
}; };
quote_spanned! {span=> quote_spanned! {ident.span()=>
let #type_properties = #type_properties.variant(#variant); let #type_properties = #type_properties.variant(#variant);
} }
})); }));
quote_spanned! {span=> quote_spanned! {ident.span()=>
#[automatically_derived] #[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType impl #static_impl_generics ::fayalite::ty::StaticType
for #target #static_type_generics for #target #static_type_generics

View file

@ -1,138 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
hdl_type_common::{
get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType,
TypesParser,
},
kw, Errors, HdlAttr,
};
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{parse_quote_spanned, Attribute, Generics, Ident, ItemType, Token, Type, Visibility};
#[derive(Clone, Debug)]
pub(crate) struct ParsedTypeAlias {
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
pub(crate) vis: Visibility,
pub(crate) type_token: Token![type],
pub(crate) ident: Ident,
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
pub(crate) eq_token: Token![=],
pub(crate) ty: MaybeParsed<ParsedType, Type>,
pub(crate) semi_token: Token![;],
}
impl ParsedTypeAlias {
fn parse(item: ItemType) -> syn::Result<Self> {
let ItemType {
mut attrs,
vis,
type_token,
ident,
mut generics,
eq_token,
ty,
semi_token,
} = item;
let mut errors = Errors::new();
let mut options = errors
.unwrap_or_default(HdlAttr::<ItemOptions, kw::hdl>::parse_and_take_attr(
&mut attrs,
))
.unwrap_or_default();
errors.ok(options.body.validate());
let ItemOptions {
outline_generated: _,
target: _,
custom_bounds,
no_static,
no_runtime_generics: _,
cmp_eq,
} = options.body;
if let Some((no_static,)) = no_static {
errors.error(no_static, "no_static is not valid on type aliases");
}
if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
}
let generics = if custom_bounds.is_some() {
MaybeParsed::Unrecognized(generics)
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
MaybeParsed::Parsed(generics)
} else {
MaybeParsed::Unrecognized(generics)
};
let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors);
errors.finish()?;
Ok(Self {
attrs,
options,
vis,
type_token,
ident,
generics,
eq_token,
ty,
semi_token,
})
}
}
impl ToTokens for ParsedTypeAlias {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
options,
vis,
type_token,
ident,
generics,
eq_token,
ty,
semi_token,
} = self;
let ItemOptions {
outline_generated: _,
target,
custom_bounds: _,
no_static: _,
no_runtime_generics,
cmp_eq: _,
} = &options.body;
let target = get_target(target, ident);
let mut type_attrs = attrs.clone();
type_attrs.push(parse_quote_spanned! {ident.span()=>
#[allow(type_alias_bounds)]
});
ItemType {
attrs: type_attrs,
vis: vis.clone(),
type_token: *type_token,
ident: ident.clone(),
generics: generics.into(),
eq_token: *eq_token,
ty: Box::new(ty.clone().into()),
semi_token: *semi_token,
}
.to_tokens(tokens);
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(ty), None) =
(generics, ty, no_runtime_generics)
{
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
ty.make_hdl_type_expr(context)
})
}
}
}
pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result<TokenStream> {
let item = ParsedTypeAlias::parse(item)?;
let outline_generated = item.options.body.outline_generated;
let mut contents = item.to_token_stream();
if outline_generated.is_some() {
contents = crate::outline_generated(contents, "hdl-type-alias-");
}
Ok(contents)
}

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{fold::impl_fold, kw, Errors, HdlAttr, PairsIterExt}; use crate::{fold::impl_fold, kw, Errors, HdlAttr, PairsIterExt};
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote_spanned, ToTokens}; use quote::{format_ident, quote_spanned, ToTokens};
@ -26,7 +24,6 @@ crate::options! {
CustomBounds(custom_bounds), CustomBounds(custom_bounds),
NoStatic(no_static), NoStatic(no_static),
NoRuntimeGenerics(no_runtime_generics), NoRuntimeGenerics(no_runtime_generics),
CmpEq(cmp_eq),
} }
} }
@ -67,7 +64,6 @@ impl Drop for WrappedInConst<'_> {
fn drop(&mut self) { fn drop(&mut self) {
let inner = &self.inner; let inner = &self.inner;
quote_spanned! {self.span=> quote_spanned! {self.span=>
#[allow(clippy::type_complexity)]
const _: () = { const _: () = {
#inner #inner
}; };
@ -1274,130 +1270,6 @@ make_parsed_type_or_const! {
} }
} }
#[derive(Debug, Clone)]
pub(crate) struct ParsedTypePhantomData {
pub(crate) phantom_data: known_items::PhantomData,
pub(crate) lt_token: Token![<],
pub(crate) ty: Box<ParsedType>,
pub(crate) gt_token: Token![>],
}
impl_fold! {
struct ParsedTypePhantomData<> {
phantom_data: known_items::PhantomData,
lt_token: Token![<],
ty: Box<ParsedType>,
gt_token: Token![>],
}
}
impl ParsedTypePhantomData {
pub(crate) fn try_from_named(
named: ParsedTypeNamed,
parser: &mut TypesParser<'_>,
) -> Result<Result<Self, ParsedTypeNamed>, ParseFailed> {
let ParsedTypeNamed { path, args } = named;
let parsed_path = known_items::PhantomData::parse_path(path);
let phantom_data = match parsed_path {
Ok(phantom_data) => phantom_data,
Err(path) => return Ok(Err(ParsedTypeNamed { path, args })),
};
let Some(ParsedGenericArguments {
colon2_token: _,
lt_token,
args,
gt_token,
}) = args
else {
parser
.errors()
.error(phantom_data, "PhantomData requires generic arguments");
return Err(ParseFailed);
};
let args_len = args.len();
if args_len != 1 {
parser.errors().error(
phantom_data,
format_args!(
"wrong number of generic arguments supplied: got {args_len}, expected 1"
),
);
return Err(ParseFailed);
}
let ty = args.into_iter().next().unwrap();
let ParsedGenericArgument::Type(ty) = ty else {
parser.errors().error(ty, "expected a type");
return Err(ParseFailed);
};
Ok(Ok(Self {
phantom_data,
lt_token,
ty: Box::new(ty),
gt_token,
}))
}
}
impl From<ParsedTypePhantomData> for Type {
fn from(value: ParsedTypePhantomData) -> Type {
let ParsedTypePhantomData {
phantom_data,
lt_token,
ty,
gt_token,
} = value;
let path = phantom_data.path;
let mut args = Punctuated::new();
args.push(GenericArgument::Type(ty.into()));
let args = AngleBracketedGenericArguments {
colon2_token: Some(Token![::](lt_token.span)),
lt_token,
args,
gt_token,
};
let mut segments = path.segments;
segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(args);
Type::Path(TypePath {
qself: None,
path: Path {
leading_colon: path.leading_colon,
segments,
},
})
}
}
impl MakeHdlTypeExpr for ParsedTypePhantomData {
fn make_hdl_type_expr(&self, _context: &MakeHdlTypeExprContext) -> Expr {
let ParsedTypePhantomData {
phantom_data,
lt_token: _,
ty: _,
gt_token: _,
} = self;
Expr::Path(ExprPath {
attrs: vec![],
qself: None,
path: phantom_data.path.clone(),
})
}
}
impl ToTokens for ParsedTypePhantomData {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
phantom_data,
lt_token,
ty,
gt_token,
} = self;
phantom_data.to_tokens(tokens);
lt_token.to_tokens(tokens);
ty.to_tokens(tokens);
gt_token.to_tokens(tokens);
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum ParsedType { pub(crate) enum ParsedType {
Delimited(ParsedTypeDelimited), Delimited(ParsedTypeDelimited),
@ -1405,7 +1277,6 @@ pub(crate) enum ParsedType {
NamedParam(ParsedTypeNamedParam), NamedParam(ParsedTypeNamedParam),
Tuple(ParsedTypeTuple), Tuple(ParsedTypeTuple),
ConstUsize(ParsedTypeConstUsize), ConstUsize(ParsedTypeConstUsize),
PhantomData(ParsedTypePhantomData),
Array(ParsedTypeArray), Array(ParsedTypeArray),
UInt(ParsedTypeUInt), UInt(ParsedTypeUInt),
SInt(ParsedTypeSInt), SInt(ParsedTypeSInt),
@ -1420,7 +1291,6 @@ impl_fold! {
NamedParam(ParsedTypeNamedParam), NamedParam(ParsedTypeNamedParam),
Tuple(ParsedTypeTuple), Tuple(ParsedTypeTuple),
ConstUsize(ParsedTypeConstUsize), ConstUsize(ParsedTypeConstUsize),
PhantomData(ParsedTypePhantomData),
Array(ParsedTypeArray), Array(ParsedTypeArray),
UInt(ParsedTypeUInt), UInt(ParsedTypeUInt),
SInt(ParsedTypeSInt), SInt(ParsedTypeSInt),
@ -1437,7 +1307,6 @@ impl From<ParsedType> for Type {
ParsedType::NamedParam(v) => v.into(), ParsedType::NamedParam(v) => v.into(),
ParsedType::Tuple(v) => v.into(), ParsedType::Tuple(v) => v.into(),
ParsedType::ConstUsize(v) => v.into(), ParsedType::ConstUsize(v) => v.into(),
ParsedType::PhantomData(v) => v.into(),
ParsedType::Array(v) => v.into(), ParsedType::Array(v) => v.into(),
ParsedType::UInt(v) => v.into(), ParsedType::UInt(v) => v.into(),
ParsedType::SInt(v) => v.into(), ParsedType::SInt(v) => v.into(),
@ -1488,7 +1357,6 @@ impl ToTokens for ParsedType {
ParsedType::Named(ty) => ty.to_tokens(tokens), ParsedType::Named(ty) => ty.to_tokens(tokens),
ParsedType::Tuple(ty) => ty.to_tokens(tokens), ParsedType::Tuple(ty) => ty.to_tokens(tokens),
ParsedType::ConstUsize(ty) => ty.to_tokens(tokens), ParsedType::ConstUsize(ty) => ty.to_tokens(tokens),
ParsedType::PhantomData(ty) => ty.to_tokens(tokens),
ParsedType::Array(ty) => ty.to_tokens(tokens), ParsedType::Array(ty) => ty.to_tokens(tokens),
ParsedType::UInt(ty) => ty.to_tokens(tokens), ParsedType::UInt(ty) => ty.to_tokens(tokens),
ParsedType::SInt(ty) => ty.to_tokens(tokens), ParsedType::SInt(ty) => ty.to_tokens(tokens),
@ -1540,7 +1408,7 @@ impl ParseTypes<Path> for ParsedType {
let mut args = None; let mut args = None;
let segments = Punctuated::from_iter(segments.pairs_mut().map_pair_value_mut(|segment| { let segments = Punctuated::from_iter(segments.pairs_mut().map_pair_value_mut(|segment| {
let PathSegment { ident, arguments } = segment; let PathSegment { ident, arguments } = segment;
if args.is_some() { if let Some(_) = args {
parser parser
.errors() .errors()
.error(&ident, "associated types/consts are not yet implemented"); .error(&ident, "associated types/consts are not yet implemented");
@ -1596,10 +1464,6 @@ impl ParseTypes<Path> for ParsedType {
Ok(v) => return Ok(Self::ConstUsize(v)), Ok(v) => return Ok(Self::ConstUsize(v)),
Err(named) => named, Err(named) => named,
}; };
let named = match ParsedTypePhantomData::try_from_named(named, parser)? {
Ok(v) => return Ok(Self::PhantomData(v)),
Err(named) => named,
};
let named = match ParsedTypeUInt::try_from_named(named, parser)? { let named = match ParsedTypeUInt::try_from_named(named, parser)? {
Ok(v) => return Ok(Self::UInt(v)), Ok(v) => return Ok(Self::UInt(v)),
Err(named) => named, Err(named) => named,
@ -1728,7 +1592,7 @@ impl ParseTypes<Path> for ParsedConstGenericType {
let mut args = None; let mut args = None;
let segments = Punctuated::from_iter(segments.pairs_mut().map_pair_value_mut(|segment| { let segments = Punctuated::from_iter(segments.pairs_mut().map_pair_value_mut(|segment| {
let PathSegment { ident, arguments } = segment; let PathSegment { ident, arguments } = segment;
if args.is_some() { if let Some(_) = args {
parser parser
.errors() .errors()
.error(&ident, "associated types/consts are not yet implemented"); .error(&ident, "associated types/consts are not yet implemented");
@ -1881,7 +1745,7 @@ impl<T: ParseTypes<I>, I, P: Clone> ParseTypes<Punctuated<I, P>> for Punctuated<
pub(crate) enum UnparsedGenericParam { pub(crate) enum UnparsedGenericParam {
Type { Type {
attrs: Vec<Attribute>, attrs: Vec<Attribute>,
options: HdlAttr<TypeParamOptions, kw::hdl>, options: HdlAttr<TypeParamOptions>,
ident: Ident, ident: Ident,
colon_token: Token![:], colon_token: Token![:],
bounds: ParsedBounds, bounds: ParsedBounds,
@ -1889,7 +1753,7 @@ pub(crate) enum UnparsedGenericParam {
}, },
Const { Const {
attrs: Vec<Attribute>, attrs: Vec<Attribute>,
options: HdlAttr<ConstParamOptions, kw::hdl>, options: HdlAttr<ConstParamOptions>,
const_token: Token![const], const_token: Token![const],
ident: Ident, ident: Ident,
colon_token: Token![:], colon_token: Token![:],
@ -1917,7 +1781,7 @@ pub(crate) mod known_items {
#[allow(non_snake_case, dead_code)] #[allow(non_snake_case, dead_code)]
pub(crate) fn $known_item(span: Span) -> $known_item { pub(crate) fn $known_item(span: Span) -> $known_item {
let segments = $known_item::PATH_SEGMENTS[0].iter() let segments = $known_item::PATH_SEGMENTS.iter()
.copied() .copied()
.map(|seg| PathSegment::from(Ident::new(seg, span))) .map(|seg| PathSegment::from(Ident::new(seg, span)))
.collect(); .collect();
@ -1943,21 +1807,20 @@ pub(crate) mod known_items {
return Ok(Self { span: ident.span(), path }); return Ok(Self { span: ident.span(), path });
} }
} }
for &path_segments in Self::PATH_SEGMENTS.iter() { if path.segments.len() == Self::PATH_SEGMENTS.len()
if path.segments.len() == path_segments.len() && path
&& path .segments
.segments .iter()
.iter() .zip(Self::PATH_SEGMENTS)
.zip(path_segments) .all(|(seg, expected)| {
.all(|(seg, expected)| { matches!(seg.arguments, PathArguments::None)
matches!(seg.arguments, PathArguments::None) && seg.ident == *expected
&& seg.ident == *expected })
}) {
{ Ok(Self { span: path.segments.last().unwrap().ident.span(), path })
return Ok(Self { span: path.segments.last().unwrap().ident.span(), path }); } else {
} Err(path)
} }
Err(path)
} }
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn parse_path_with_arguments(mut path: Path) -> Result<(Self, PathArguments), Path> { pub(crate) fn parse_path_with_arguments(mut path: Path) -> Result<(Self, PathArguments), Path> {
@ -2012,31 +1875,25 @@ pub(crate) mod known_items {
} }
macro_rules! impl_known_item { macro_rules! impl_known_item {
($(#[alias = $(::$alias:ident)+])* [$(::$seg:ident)+] ::$known_item:ident) => { ($([$(::$head:ident)*])? ::$next:ident $(::$tail:ident)+) => {
impl_known_item!([$($(::$head)*)? ::$next] $(::$tail)+);
};
([$(::$seg:ident)+] ::$known_item:ident) => {
impl_known_item_body!($known_item); impl_known_item_body!($known_item);
impl $known_item { impl $known_item {
pub(crate) const PATH_SEGMENTS: &'static [&'static [&'static str]] = &[ pub(crate) const PATH_SEGMENTS: &'static [&'static str] = &[
&[ $(stringify!($seg),)+
$(stringify!($seg),)+ stringify!($known_item),
stringify!($known_item),
],
$(&[
$(stringify!($alias),)+
],)*
]; ];
} }
}; };
($(#[alias = $(::$alias:ident)+])* $([$(::$head:ident)*])? ::$next:ident $(::$tail:ident)+) => {
impl_known_item!($(#[alias = $(::$alias)+])* [$($(::$head)*)? ::$next] $(::$tail)+);
};
} }
impl_known_item!(::fayalite::array::Array); impl_known_item!(::fayalite::array::Array);
impl_known_item!(::fayalite::array::ArrayType); impl_known_item!(::fayalite::array::ArrayType);
impl_known_item!(::fayalite::bundle::BundleType); impl_known_item!(::fayalite::bundle::BundleType);
impl_known_item!(::fayalite::enum_::EnumType); impl_known_item!(::fayalite::enum_::EnumType);
impl_known_item!(::fayalite::int::BoolOrIntType);
impl_known_item!(::fayalite::int::DynSize); impl_known_item!(::fayalite::int::DynSize);
impl_known_item!(::fayalite::int::IntType); impl_known_item!(::fayalite::int::IntType);
impl_known_item!(::fayalite::int::KnownSize); impl_known_item!(::fayalite::int::KnownSize);
@ -2045,22 +1902,12 @@ 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::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);
impl_known_item!(::fayalite::ty::Type); impl_known_item!(::fayalite::ty::Type);
impl_known_item!(::fayalite::ty::Type::MaskType); impl_known_item!(::fayalite::ty::Type::MaskType);
impl_known_item!(::fayalite::util::ConstUsize); impl_known_item!(::fayalite::util::ConstUsize);
impl_known_item!( impl_known_item!(::fayalite::__std::primitive::usize);
#[alias = ::std::primitive::usize]
#[alias = ::core::primitive::usize]
::fayalite::__std::primitive::usize
);
impl_known_item!(
#[alias = ::std::marker::PhantomData]
#[alias = ::core::marker::PhantomData]
::fayalite::__std::marker::PhantomData
);
} }
macro_rules! impl_bounds { macro_rules! impl_bounds {
@ -2070,16 +1917,11 @@ macro_rules! impl_bounds {
$( $(
$Variant:ident, $Variant:ident,
)* )*
$(
#[unknown]
$Unknown:ident,
)?
} }
) => { ) => {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
$vis enum $enum_type { $vis enum $enum_type {
$($Variant(known_items::$Variant),)* $($Variant(known_items::$Variant),)*
$($Unknown(syn::TypeParamBound),)?
} }
$(impl From<known_items::$Variant> for $enum_type { $(impl From<known_items::$Variant> for $enum_type {
@ -2092,54 +1934,28 @@ macro_rules! impl_bounds {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
match self { match self {
$(Self::$Variant(v) => v.to_tokens(tokens),)* $(Self::$Variant(v) => v.to_tokens(tokens),)*
$(Self::$Unknown(v) => v.to_tokens(tokens),)?
} }
} }
} }
impl $enum_type { impl $enum_type {
$vis fn parse_path(path: Path) -> Result<Self, Path> { $vis fn parse_path(path: Path) -> Result<Self, Path> {
#![allow(unreachable_code)]
$(let path = match known_items::$Variant::parse_path(path) { $(let path = match known_items::$Variant::parse_path(path) {
Ok(v) => return Ok(Self::$Variant(v)), Ok(v) => return Ok(Self::$Variant(v)),
Err(path) => path, Err(path) => path,
};)* };)*
$(return Ok(Self::$Unknown(syn::TraitBound {
paren_token: None,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path,
}.into()));)?
Err(path) Err(path)
} }
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> {
#![allow(unreachable_code)]
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
if let syn::TraitBound {
paren_token: _,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path: _,
} = trait_bound {
match Self::parse_path(trait_bound.path) {
Ok(retval) => return Ok(retval),
Err(path) => trait_bound.path = path,
}
}
type_param_bound = trait_bound.into();
}
$(return Ok(Self::$Unknown(type_param_bound));)?
Err(type_param_bound)
}
} }
impl Parse for $enum_type { impl Parse for $enum_type {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
Self::parse_type_param_bound(input.parse()?) Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| {
.map_err(|type_param_bound| syn::Error::new_spanned( syn::Error::new_spanned(
type_param_bound, path,
format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
)) )
})
} }
} }
@ -2147,7 +1963,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 $Unknown: Vec<syn::TypeParamBound>,)?
} }
impl ToTokens for $struct_type { impl ToTokens for $struct_type {
@ -2159,63 +1974,42 @@ macro_rules! impl_bounds {
separator = Some(<Token![+]>::default()); separator = Some(<Token![+]>::default());
v.to_tokens(tokens); v.to_tokens(tokens);
})* })*
$(for v in &self.$Unknown {
separator.to_tokens(tokens);
separator = Some(<Token![+]>::default());
v.to_tokens(tokens);
})*
} }
} }
const _: () = { const _: () = {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[allow(non_snake_case)] $vis struct Iter($vis $struct_type);
$vis struct Iter {
$($Variant: Option<known_items::$Variant>,)*
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
}
impl IntoIterator for $struct_type { impl IntoIterator for $struct_type {
type Item = $enum_type; type Item = $enum_type;
type IntoIter = Iter; type IntoIter = Iter;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
Iter { Iter(self)
$($Variant: self.$Variant,)*
$($Unknown: self.$Unknown.into_iter(),)?
}
} }
} }
impl Iterator for Iter { impl Iterator for Iter {
type Item = $enum_type; type Item = $enum_type;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
$( $(
if let Some(value) = self.$Variant.take() { if let Some(value) = self.0.$Variant.take() {
return Some($enum_type::$Variant(value)); return Some($enum_type::$Variant(value));
} }
)* )*
$(
if let Some(value) = self.$Unknown.next() {
return Some($enum_type::$Unknown(value));
}
)?
None None
} }
#[allow(unused_mut, unused_variables)] #[allow(unused_mut, unused_variables)]
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B { fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
$( $(
if let Some(value) = self.$Variant.take() { if let Some(value) = self.0.$Variant.take() {
init = f(init, $enum_type::$Variant(value)); init = f(init, $enum_type::$Variant(value));
} }
)* )*
$(
if let Some(value) = self.$Unknown.next() {
init = f(init, $enum_type::$Unknown(value));
}
)?
init init
} }
} }
@ -2227,9 +2021,6 @@ macro_rules! impl_bounds {
$($enum_type::$Variant(v) => { $($enum_type::$Variant(v) => {
self.$Variant = Some(v); self.$Variant = Some(v);
})* })*
$($enum_type::$Unknown(v) => {
self.$Unknown.push(v);
})?
}); });
} }
} }
@ -2248,7 +2039,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);
})* })*
$(self.$Unknown.extend(v.$Unknown);)*
}); });
} }
} }
@ -2293,46 +2083,35 @@ macro_rules! impl_bounds {
impl_bounds! { impl_bounds! {
#[struct = ParsedBounds] #[struct = ParsedBounds]
pub(crate) enum ParsedBound { pub(crate) enum ParsedBound {
BoolOrIntType,
BundleType, BundleType,
EnumType, EnumType,
IntType, IntType,
KnownSize, KnownSize,
ResetType,
Size, Size,
StaticType, StaticType,
Type, Type,
#[unknown]
Unknown,
} }
} }
impl_bounds! { impl_bounds! {
#[struct = ParsedTypeBounds] #[struct = ParsedTypeBounds]
pub(crate) enum ParsedTypeBound { pub(crate) enum ParsedTypeBound {
BoolOrIntType,
BundleType, BundleType,
EnumType, EnumType,
IntType, IntType,
ResetType,
StaticType, StaticType,
Type, Type,
#[unknown]
Unknown,
} }
} }
impl From<ParsedTypeBound> for ParsedBound { impl From<ParsedTypeBound> for ParsedBound {
fn from(value: ParsedTypeBound) -> Self { fn from(value: ParsedTypeBound) -> Self {
match value { match value {
ParsedTypeBound::BoolOrIntType(v) => ParsedBound::BoolOrIntType(v),
ParsedTypeBound::BundleType(v) => ParsedBound::BundleType(v), ParsedTypeBound::BundleType(v) => ParsedBound::BundleType(v),
ParsedTypeBound::EnumType(v) => ParsedBound::EnumType(v), ParsedTypeBound::EnumType(v) => ParsedBound::EnumType(v),
ParsedTypeBound::IntType(v) => ParsedBound::IntType(v), ParsedTypeBound::IntType(v) => ParsedBound::IntType(v),
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
ParsedTypeBound::Type(v) => ParsedBound::Type(v), ParsedTypeBound::Type(v) => ParsedBound::Type(v),
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
} }
} }
} }
@ -2340,26 +2119,20 @@ impl From<ParsedTypeBound> for ParsedBound {
impl From<ParsedTypeBounds> for ParsedBounds { impl From<ParsedTypeBounds> for ParsedBounds {
fn from(value: ParsedTypeBounds) -> Self { fn from(value: ParsedTypeBounds) -> Self {
let ParsedTypeBounds { let ParsedTypeBounds {
BoolOrIntType,
BundleType, BundleType,
EnumType, EnumType,
IntType, IntType,
ResetType,
StaticType, StaticType,
Type, Type,
Unknown,
} = value; } = value;
Self { Self {
BoolOrIntType,
BundleType, BundleType,
EnumType, EnumType,
IntType, IntType,
KnownSize: None, KnownSize: None,
ResetType,
Size: None, Size: None,
StaticType, StaticType,
Type, Type,
Unknown,
} }
} }
} }
@ -2368,10 +2141,6 @@ impl ParsedTypeBound {
fn implied_bounds(self) -> ParsedTypeBounds { fn implied_bounds(self) -> ParsedTypeBounds {
let span = self.span(); let span = self.span();
match self { match self {
Self::BoolOrIntType(v) => ParsedTypeBounds::from_iter([
ParsedTypeBound::from(v),
ParsedTypeBound::Type(known_items::Type(span)),
]),
Self::BundleType(v) => ParsedTypeBounds::from_iter([ Self::BundleType(v) => ParsedTypeBounds::from_iter([
ParsedTypeBound::from(v), ParsedTypeBound::from(v),
ParsedTypeBound::Type(known_items::Type(span)), ParsedTypeBound::Type(known_items::Type(span)),
@ -2382,12 +2151,6 @@ impl ParsedTypeBound {
]), ]),
Self::IntType(v) => ParsedTypeBounds::from_iter([ Self::IntType(v) => ParsedTypeBounds::from_iter([
ParsedTypeBound::from(v), ParsedTypeBound::from(v),
ParsedTypeBound::BoolOrIntType(known_items::BoolOrIntType(span)),
ParsedTypeBound::Type(known_items::Type(span)),
]),
Self::ResetType(v) => ParsedTypeBounds::from_iter([
ParsedTypeBound::from(v),
ParsedTypeBound::StaticType(known_items::StaticType(span)),
ParsedTypeBound::Type(known_items::Type(span)), ParsedTypeBound::Type(known_items::Type(span)),
]), ]),
Self::StaticType(v) => ParsedTypeBounds::from_iter([ Self::StaticType(v) => ParsedTypeBounds::from_iter([
@ -2395,7 +2158,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::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
} }
} }
} }
@ -2421,16 +2183,13 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
fn from(value: ParsedSizeTypeBounds) -> Self { fn from(value: ParsedSizeTypeBounds) -> Self {
let ParsedSizeTypeBounds { KnownSize, Size } = value; let ParsedSizeTypeBounds { KnownSize, Size } = value;
Self { Self {
BoolOrIntType: None,
BundleType: None, BundleType: None,
EnumType: None, EnumType: None,
IntType: None, IntType: None,
KnownSize, KnownSize,
ResetType: None,
Size, Size,
StaticType: None, StaticType: None,
Type: None, Type: None,
Unknown: vec![],
} }
} }
} }
@ -2458,7 +2217,6 @@ impl ParsedBounds {
fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory {
let mut type_bounds = None; let mut type_bounds = None;
let mut size_type_bounds = None; let mut size_type_bounds = None;
let mut unknown_bounds = vec![];
self.into_iter().for_each(|bound| match bound.categorize() { self.into_iter().for_each(|bound| match bound.categorize() {
ParsedBoundCategory::Type(bound) => { ParsedBoundCategory::Type(bound) => {
type_bounds type_bounds
@ -2470,37 +2228,15 @@ impl ParsedBounds {
.get_or_insert_with(ParsedSizeTypeBounds::default) .get_or_insert_with(ParsedSizeTypeBounds::default)
.extend([bound]); .extend([bound]);
} }
ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound),
}); });
match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) { match (type_bounds, size_type_bounds) {
(None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds { (None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds {
Type: Some(known_items::Type(span)), Type: Some(known_items::Type(span)),
..Default::default() ..Default::default()
}), }),
(None, None, false) => { (None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds),
errors.error( (Some(bounds), None) => ParsedBoundsCategory::Type(bounds),
unknown_bounds.remove(0), (Some(type_bounds), Some(size_type_bounds)) => {
"unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds",
);
ParsedBoundsCategory::Type(ParsedTypeBounds {
Unknown: unknown_bounds,
..Default::default()
})
}
(None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds),
(None, Some(bounds), false) => {
// TODO: implement
errors.error(
unknown_bounds.remove(0),
"unknown bounds with `Size` bounds are not implemented",
);
ParsedBoundsCategory::SizeType(bounds)
}
(Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds {
Unknown: unknown_bounds,
..bounds
}),
(Some(type_bounds), Some(size_type_bounds), _) => {
errors.error( errors.error(
size_type_bounds size_type_bounds
.Size .Size
@ -2517,29 +2253,24 @@ impl ParsedBounds {
pub(crate) enum ParsedBoundCategory { pub(crate) enum ParsedBoundCategory {
Type(ParsedTypeBound), Type(ParsedTypeBound),
SizeType(ParsedSizeTypeBound), SizeType(ParsedSizeTypeBound),
Unknown(syn::TypeParamBound),
} }
impl ParsedBound { impl ParsedBound {
fn categorize(self) -> ParsedBoundCategory { fn categorize(self) -> ParsedBoundCategory {
match self { match self {
Self::BoolOrIntType(v) => ParsedBoundCategory::Type(ParsedTypeBound::BoolOrIntType(v)),
Self::BundleType(v) => ParsedBoundCategory::Type(ParsedTypeBound::BundleType(v)), Self::BundleType(v) => ParsedBoundCategory::Type(ParsedTypeBound::BundleType(v)),
Self::EnumType(v) => ParsedBoundCategory::Type(ParsedTypeBound::EnumType(v)), Self::EnumType(v) => ParsedBoundCategory::Type(ParsedTypeBound::EnumType(v)),
Self::IntType(v) => ParsedBoundCategory::Type(ParsedTypeBound::IntType(v)), Self::IntType(v) => ParsedBoundCategory::Type(ParsedTypeBound::IntType(v)),
Self::KnownSize(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::KnownSize(v)), Self::KnownSize(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::KnownSize(v)),
Self::ResetType(v) => ParsedBoundCategory::Type(ParsedTypeBound::ResetType(v)),
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
} }
} }
fn implied_bounds(self) -> ParsedBounds { fn implied_bounds(self) -> ParsedBounds {
match self.categorize() { match self.categorize() {
ParsedBoundCategory::Type(v) => v.implied_bounds().into(), ParsedBoundCategory::Type(v) => v.implied_bounds().into(),
ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(), ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(),
ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]),
} }
} }
} }
@ -2547,7 +2278,7 @@ impl ParsedBound {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ParsedTypeParam { pub(crate) struct ParsedTypeParam {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<TypeParamOptions, kw::hdl>, pub(crate) options: HdlAttr<TypeParamOptions>,
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) colon_token: Token![:], pub(crate) colon_token: Token![:],
pub(crate) bounds: ParsedTypeBounds, pub(crate) bounds: ParsedTypeBounds,
@ -2581,7 +2312,7 @@ impl ToTokens for ParsedTypeParam {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ParsedSizeTypeParam { pub(crate) struct ParsedSizeTypeParam {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<TypeParamOptions, kw::hdl>, pub(crate) options: HdlAttr<TypeParamOptions>,
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) colon_token: Token![:], pub(crate) colon_token: Token![:],
pub(crate) bounds: ParsedSizeTypeBounds, pub(crate) bounds: ParsedSizeTypeBounds,
@ -2625,7 +2356,7 @@ pub(crate) struct ParsedConstParamWhereBounds {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ParsedConstParam { pub(crate) struct ParsedConstParam {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ConstParamOptions, kw::hdl>, pub(crate) options: HdlAttr<ConstParamOptions>,
pub(crate) const_token: Token![const], pub(crate) const_token: Token![const],
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) colon_token: Token![:], pub(crate) colon_token: Token![:],
@ -2682,7 +2413,7 @@ impl ParsedGenericParam {
} }
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone)]
pub(crate) struct ParsedGenerics { pub(crate) struct ParsedGenerics {
pub(crate) lt_token: Option<Token![<]>, pub(crate) lt_token: Option<Token![<]>,
pub(crate) params: Punctuated<ParsedGenericParam, Token![,]>, pub(crate) params: Punctuated<ParsedGenericParam, Token![,]>,
@ -2842,7 +2573,6 @@ impl ParsedGenerics {
} }
}) })
.collect(); .collect();
let param_token = Ident::new("__param", ident.span());
for (param_count, (generics_accumulation_type, next_param)) in generics_accumulation_types for (param_count, (generics_accumulation_type, next_param)) in generics_accumulation_types
.iter() .iter()
.zip(&self.params) .zip(&self.params)
@ -2891,7 +2621,7 @@ impl ParsedGenerics {
next_generics.split_for_impl(); next_generics.split_for_impl();
let next_turbofish = next_type_generics.as_turbofish(); let next_turbofish = next_type_generics.as_turbofish();
let mut param: Expr = parse_quote_spanned! {ident.span()=> let mut param: Expr = parse_quote_spanned! {ident.span()=>
#param_token __param
}; };
let mut generics = next_generics.clone(); let mut generics = next_generics.clone();
let mut index_type = param_ident.clone(); let mut index_type = param_ident.clone();
@ -2906,7 +2636,7 @@ impl ParsedGenerics {
is_const: false, is_const: false,
}); });
param = parse_quote_spanned! {ident.span()=> param = parse_quote_spanned! {ident.span()=>
::fayalite::ty::TypeOrDefault::get(#param_token, || #default_expr) ::fayalite::ty::TypeOrDefault::get(__param, || #default_expr)
}; };
let context = MakeHdlTypeExprContext { let context = MakeHdlTypeExprContext {
named_param_values: self_members[..param_count] named_param_values: self_members[..param_count]
@ -2971,8 +2701,8 @@ impl ParsedGenerics {
{ {
type Output = #next_target #next_type_generics; type Output = #next_target #next_type_generics;
fn index(&self, #param_token: #index_type) -> &Self::Output { fn index(&self, __param: #index_type) -> &Self::Output {
::fayalite::intern::Interned::into_inner( ::fayalite::intern::Interned::<_>::into_inner(
::fayalite::intern::Intern::intern_sized(#output_expr), ::fayalite::intern::Intern::intern_sized(#output_expr),
) )
} }
@ -2992,7 +2722,7 @@ impl ParsedGenerics {
.iter() .iter()
.cloned() .cloned()
.chain([parse_quote_spanned! {ident.span()=> .chain([parse_quote_spanned! {ident.span()=>
#param_token __param
}]) }])
.collect(), .collect(),
is_const: false, is_const: false,
@ -3031,8 +2761,8 @@ impl ParsedGenerics {
{ {
type Output = #next_target #next_target_args; type Output = #next_target #next_target_args;
fn index(&self, #param_token: #param_ident) -> &Self::Output { fn index(&self, __param: #param_ident) -> &Self::Output {
::fayalite::intern::Interned::into_inner( ::fayalite::intern::Interned::<_>::into_inner(
::fayalite::intern::Intern::intern_sized(#output_expr), ::fayalite::intern::Intern::intern_sized(#output_expr),
) )
} }
@ -3059,7 +2789,7 @@ impl ParsedGenerics {
.iter() .iter()
.cloned() .cloned()
.chain([parse_quote_spanned! {ident.span()=> .chain([parse_quote_spanned! {ident.span()=>
#param_token __param
}]) }])
.collect(), .collect(),
is_const: false, is_const: false,
@ -3101,8 +2831,8 @@ impl ParsedGenerics {
{ {
type Output = #next_target #next_target_args; type Output = #next_target #next_target_args;
fn index(&self, #param_token: __Param) -> &Self::Output { fn index(&self, __param: __Param) -> &Self::Output {
::fayalite::intern::Interned::into_inner( ::fayalite::intern::Interned::<_>::into_inner(
::fayalite::intern::Intern::intern_sized(#output_expr), ::fayalite::intern::Intern::intern_sized(#output_expr),
) )
} }
@ -3133,11 +2863,9 @@ impl ParsedGenerics {
let (input_param, punct) = input_param.into_tuple(); let (input_param, punct) = input_param.into_tuple();
let (unparsed_param, late_parsed_param) = match input_param { let (unparsed_param, late_parsed_param) = match input_param {
GenericParam::Lifetime(param) => { GenericParam::Lifetime(param) => {
errors.unwrap_or_default( errors.unwrap_or_default(HdlAttr::<LifetimeParamOptions>::parse_and_take_attr(
HdlAttr::<LifetimeParamOptions, kw::hdl>::parse_and_take_attr( &mut param.attrs,
&mut param.attrs, ));
),
);
errors.error(param, "lifetime generics are not supported by #[hdl]"); errors.error(param, "lifetime generics are not supported by #[hdl]");
continue; continue;
} }
@ -3151,9 +2879,7 @@ impl ParsedGenerics {
}) => { }) => {
let span = ident.span(); let span = ident.span();
let options = errors let options = errors
.unwrap_or_default( .unwrap_or_default(HdlAttr::<TypeParamOptions>::parse_and_take_attr(attrs))
HdlAttr::<TypeParamOptions, kw::hdl>::parse_and_take_attr(attrs),
)
.unwrap_or_default(); .unwrap_or_default();
let colon_token = colon_token.unwrap_or_else(|| Token![:](span)); let colon_token = colon_token.unwrap_or_else(|| Token![:](span));
if !bounds.is_empty() { if !bounds.is_empty() {
@ -3191,9 +2917,7 @@ impl ParsedGenerics {
default, default,
}) => { }) => {
let options = errors let options = errors
.unwrap_or_default( .unwrap_or_default(HdlAttr::<ConstParamOptions>::parse_and_take_attr(attrs))
HdlAttr::<ConstParamOptions, kw::hdl>::parse_and_take_attr(attrs),
)
.unwrap_or_default(); .unwrap_or_default();
if let Some(default) = default { if let Some(default) = default {
let _ = eq_token; let _ = eq_token;
@ -3413,29 +3137,17 @@ impl ParsedGenerics {
.Type .Type
.get_or_insert_with(|| known_items::Type(bound.span())); .get_or_insert_with(|| known_items::Type(bound.span()));
match bound { match bound {
ParsedTypeBound::BoolOrIntType(_) ParsedTypeBound::BundleType(_)
| ParsedTypeBound::BundleType(_)
| ParsedTypeBound::EnumType(_) | ParsedTypeBound::EnumType(_)
| ParsedTypeBound::IntType(_) | ParsedTypeBound::IntType(_) => {
| ParsedTypeBound::ResetType(_) => { errors.error(bound, "bound on mask type not implemented");
errors.error(bound, "bounds on mask types are not implemented");
} }
ParsedTypeBound::StaticType(bound) => { ParsedTypeBound::StaticType(bound) => {
if bounds.StaticType.is_none() { if bounds.StaticType.is_none() {
errors.error( errors.error(bound, "StaticType bound on mask type without corresponding StaticType bound on original type is not implemented");
bound,
"StaticType bound on mask type without corresponding \
StaticType bound on original type is not implemented",
);
} }
} },
ParsedTypeBound::Type(_) => {} ParsedTypeBound::Type(_) => {}
ParsedTypeBound::Unknown(_) => {
errors.error(
bound,
"unknown bounds on mask types are not implemented",
);
}
} }
} }
bounds.add_implied_bounds(); bounds.add_implied_bounds();
@ -3815,7 +3527,7 @@ impl SplitForImpl for Generics {
Self::TypeGenerics<'_>, Self::TypeGenerics<'_>,
Self::WhereClause<'_>, Self::WhereClause<'_>,
) { ) {
Generics::split_for_impl(self) Generics::split_for_impl(&self)
} }
} }
@ -4229,7 +3941,6 @@ impl MakeHdlTypeExpr for ParsedType {
Self::NamedParam(v) => v.make_hdl_type_expr(context), Self::NamedParam(v) => v.make_hdl_type_expr(context),
Self::Tuple(v) => v.make_hdl_type_expr(context), Self::Tuple(v) => v.make_hdl_type_expr(context),
Self::ConstUsize(v) => v.make_hdl_type_expr(context), Self::ConstUsize(v) => v.make_hdl_type_expr(context),
Self::PhantomData(v) => v.make_hdl_type_expr(context),
Self::Array(v) => v.make_hdl_type_expr(context), Self::Array(v) => v.make_hdl_type_expr(context),
Self::UInt(v) => v.make_hdl_type_expr(context), Self::UInt(v) => v.make_hdl_type_expr(context),
Self::SInt(v) => v.make_hdl_type_expr(context), Self::SInt(v) => v.make_hdl_type_expr(context),
@ -4276,13 +3987,7 @@ impl MakeHdlTypeExpr for ParsedExpr {
match self { match self {
ParsedExpr::Delimited(expr) => expr.make_hdl_type_expr(context), ParsedExpr::Delimited(expr) => expr.make_hdl_type_expr(context),
ParsedExpr::NamedParamConst(expr) => expr.make_hdl_type_expr(context), ParsedExpr::NamedParamConst(expr) => expr.make_hdl_type_expr(context),
ParsedExpr::Other(expr) => { ParsedExpr::Other(expr) => (**expr).clone(),
let span = expr.span();
let const_usize = known_items::ConstUsize(span);
parse_quote_spanned! {expr.span()=>
#const_usize::<{ #expr }>
}
}
} }
} }
} }

View file

@ -3,43 +3,22 @@
#![cfg_attr(test, recursion_limit = "512")] #![cfg_attr(test, recursion_limit = "512")]
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use std::{ use std::io::{ErrorKind, Write};
collections::{hash_map::Entry, HashMap},
io::{ErrorKind, Write},
};
use syn::{ use syn::{
bracketed, bracketed, parenthesized,
ext::IdentExt,
parenthesized,
parse::{Parse, ParseStream, Parser}, parse::{Parse, ParseStream, Parser},
parse_quote, parse_quote,
punctuated::{Pair, Punctuated}, punctuated::Pair,
spanned::Spanned, AttrStyle, Attribute, Error, Item, Token,
token::{Bracket, Paren},
AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token,
}; };
mod fold; mod fold;
mod hdl_bundle; mod hdl_bundle;
mod hdl_enum; mod hdl_enum;
mod hdl_type_alias;
mod hdl_type_common; mod hdl_type_common;
mod module; mod module;
mod process_cfg; //mod value_derive_common;
//mod value_derive_struct;
pub(crate) trait CustomToken:
Copy
+ Spanned
+ ToTokens
+ std::fmt::Debug
+ Eq
+ std::hash::Hash
+ Default
+ quote::IdentFragment
+ Parse
{
const IDENT_STR: &'static str;
}
mod kw { mod kw {
pub(crate) use syn::token::Extern as extern_; pub(crate) use syn::token::Extern as extern_;
@ -59,26 +38,14 @@ mod kw {
} }
crate::fold::no_op_fold!($kw); crate::fold::no_op_fold!($kw);
impl crate::CustomToken for $kw {
const IDENT_STR: &'static str = stringify!($kw);
}
}; };
} }
custom_keyword!(__evaluated_cfgs);
custom_keyword!(all);
custom_keyword!(any);
custom_keyword!(cfg);
custom_keyword!(cfg_attr);
custom_keyword!(clock_domain); custom_keyword!(clock_domain);
custom_keyword!(cmp_eq);
custom_keyword!(connect_inexact); custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds); custom_keyword!(custom_bounds);
custom_keyword!(flip); custom_keyword!(flip);
custom_keyword!(hdl); custom_keyword!(hdl);
custom_keyword!(hdl_module);
custom_keyword!(incomplete_wire);
custom_keyword!(input); custom_keyword!(input);
custom_keyword!(instance); custom_keyword!(instance);
custom_keyword!(m); custom_keyword!(m);
@ -88,11 +55,11 @@ mod kw {
custom_keyword!(no_reset); custom_keyword!(no_reset);
custom_keyword!(no_runtime_generics); custom_keyword!(no_runtime_generics);
custom_keyword!(no_static); custom_keyword!(no_static);
custom_keyword!(not);
custom_keyword!(outline_generated); custom_keyword!(outline_generated);
custom_keyword!(output); custom_keyword!(output);
custom_keyword!(reg_builder); custom_keyword!(reg_builder);
custom_keyword!(reset); custom_keyword!(reset);
custom_keyword!(reset_default);
custom_keyword!(skip); custom_keyword!(skip);
custom_keyword!(target); custom_keyword!(target);
custom_keyword!(wire); custom_keyword!(wire);
@ -101,34 +68,34 @@ mod kw {
type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676 type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct HdlAttr<T, KW> { pub(crate) struct HdlAttr<T> {
pub(crate) pound_token: Pound, pub(crate) pound_token: Pound,
pub(crate) style: AttrStyle, pub(crate) style: AttrStyle,
pub(crate) bracket_token: syn::token::Bracket, pub(crate) bracket_token: syn::token::Bracket,
pub(crate) kw: KW, pub(crate) hdl: kw::hdl,
pub(crate) paren_token: Option<syn::token::Paren>, pub(crate) paren_token: Option<syn::token::Paren>,
pub(crate) body: T, pub(crate) body: T,
} }
crate::fold::impl_fold! { crate::fold::impl_fold! {
struct HdlAttr<T, KW,> { struct HdlAttr<T,> {
pound_token: Pound, pound_token: Pound,
style: AttrStyle, style: AttrStyle,
bracket_token: syn::token::Bracket, bracket_token: syn::token::Bracket,
kw: KW, hdl: kw::hdl,
paren_token: Option<syn::token::Paren>, paren_token: Option<syn::token::Paren>,
body: T, body: T,
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
impl<T, KW> HdlAttr<T, KW> { impl<T> HdlAttr<T> {
pub(crate) fn split_body(self) -> (HdlAttr<(), KW>, T) { pub(crate) fn split_body(self) -> (HdlAttr<()>, T) {
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body, body,
} = self; } = self;
@ -137,19 +104,19 @@ impl<T, KW> HdlAttr<T, KW> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body: (), body: (),
}, },
body, body,
) )
} }
pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2, KW> { pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2> {
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body: _, body: _,
} = self; } = self;
@ -157,20 +124,17 @@ impl<T, KW> HdlAttr<T, KW> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body, body,
} }
} }
pub(crate) fn as_ref(&self) -> HdlAttr<&T, KW> pub(crate) fn as_ref(&self) -> HdlAttr<&T> {
where
KW: Clone,
{
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
ref kw, hdl,
paren_token, paren_token,
ref body, ref body,
} = *self; } = *self;
@ -178,20 +142,17 @@ impl<T, KW> HdlAttr<T, KW> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw: kw.clone(), hdl,
paren_token, paren_token,
body, body,
} }
} }
pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>( pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(self, f: F) -> Result<HdlAttr<R>, E> {
self,
f: F,
) -> Result<HdlAttr<R, KW>, E> {
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body, body,
} = self; } = self;
@ -199,17 +160,17 @@ impl<T, KW> HdlAttr<T, KW> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body: f(body)?, body: f(body)?,
}) })
} }
pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R, KW> { pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R> {
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body, body,
} = self; } = self;
@ -217,7 +178,7 @@ impl<T, KW> HdlAttr<T, KW> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body: f(body), body: f(body),
} }
@ -225,32 +186,31 @@ impl<T, KW> HdlAttr<T, KW> {
fn to_attr(&self) -> Attribute fn to_attr(&self) -> Attribute
where where
T: ToTokens, T: ToTokens,
KW: ToTokens,
{ {
parse_quote! { #self } parse_quote! { #self }
} }
} }
impl<T: Default, KW: Default> Default for HdlAttr<T, KW> { impl<T: Default> Default for HdlAttr<T> {
fn default() -> Self { fn default() -> Self {
T::default().into() T::default().into()
} }
} }
impl<T, KW: Default> From<T> for HdlAttr<T, KW> { impl<T> From<T> for HdlAttr<T> {
fn from(body: T) -> Self { fn from(body: T) -> Self {
HdlAttr { HdlAttr {
pound_token: Default::default(), pound_token: Default::default(),
style: AttrStyle::Outer, style: AttrStyle::Outer,
bracket_token: Default::default(), bracket_token: Default::default(),
kw: Default::default(), hdl: Default::default(),
paren_token: Default::default(), paren_token: Default::default(),
body, body,
} }
} }
} }
impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> { impl<T: ToTokens> ToTokens for HdlAttr<T> {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
self.pound_token.to_tokens(tokens); self.pound_token.to_tokens(tokens);
match self.style { match self.style {
@ -258,7 +218,7 @@ impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
AttrStyle::Outer => {} AttrStyle::Outer => {}
}; };
self.bracket_token.surround(tokens, |tokens| { self.bracket_token.surround(tokens, |tokens| {
self.kw.to_tokens(tokens); self.hdl.to_tokens(tokens);
match self.paren_token { match self.paren_token {
Some(paren_token) => { Some(paren_token) => {
paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens)) paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens))
@ -266,7 +226,7 @@ impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
None => { None => {
let body = self.body.to_token_stream(); let body = self.body.to_token_stream();
if !body.is_empty() { if !body.is_empty() {
syn::token::Paren(self.kw.span()) syn::token::Paren(self.hdl.span)
.surround(tokens, |tokens| tokens.extend([body])); .surround(tokens, |tokens| tokens.extend([body]));
} }
} }
@ -275,24 +235,18 @@ impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
} }
} }
fn is_hdl_attr<KW: CustomToken>(attr: &Attribute) -> bool { fn is_hdl_attr(attr: &Attribute) -> bool {
attr.path().is_ident(KW::IDENT_STR) attr.path().is_ident("hdl")
} }
impl<T: Parse, KW: Parse> HdlAttr<T, KW> { impl<T: Parse> HdlAttr<T> {
fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>> fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>> {
where
KW: ToTokens,
{
let mut retval = None; let mut retval = None;
let mut errors = Errors::new(); let mut errors = Errors::new();
attrs.retain(|attr| { attrs.retain(|attr| {
if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) { if is_hdl_attr(attr) {
if retval.is_some() { if retval.is_some() {
errors.push(Error::new_spanned( errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute"));
attr,
format_args!("more than one #[{}] attribute", kw.to_token_stream()),
));
} }
errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v))); errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
false false
@ -303,19 +257,13 @@ impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
errors.finish()?; errors.finish()?;
Ok(retval) Ok(retval)
} }
fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>> fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>> {
where
KW: ToTokens,
{
let mut retval = None; let mut retval = None;
let mut errors = Errors::new(); let mut errors = Errors::new();
for attr in attrs { for attr in attrs {
if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) { if is_hdl_attr(attr) {
if retval.is_some() { if retval.is_some() {
errors.push(Error::new_spanned( errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute"));
attr,
format_args!("more than one #[{}] attribute", kw.to_token_stream()),
));
} }
errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v))); errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
} }
@ -336,7 +284,7 @@ impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
) -> syn::Result<Self> { ) -> syn::Result<Self> {
let bracket_content; let bracket_content;
let bracket_token = bracketed!(bracket_content in input); let bracket_token = bracketed!(bracket_content in input);
let kw = bracket_content.parse()?; let hdl = bracket_content.parse()?;
let paren_content; let paren_content;
let body; let body;
let paren_token; let paren_token;
@ -357,7 +305,7 @@ impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
kw, hdl,
paren_token, paren_token,
body, body,
}) })
@ -865,7 +813,6 @@ macro_rules! options {
}; };
} }
use crate::hdl_type_alias::hdl_type_alias_impl;
pub(crate) use options; pub(crate) use options;
pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStream { pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStream {
@ -905,372 +852,25 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
} }
} }
fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> { pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let func = module::ModuleFn::parse_from_fn(item)?; let options = syn::parse2::<module::ConfigOptions>(attr)?;
let options = func.config_options(); let options = HdlAttr::from(options);
let func = syn::parse2::<module::ModuleFn>(quote! { #options #item })?;
let mut contents = func.generate(); let mut contents = func.generate();
if options.outline_generated.is_some() { if options.body.outline_generated.is_some() {
contents = outline_generated(contents, "module-"); contents = outline_generated(contents, "module-");
} }
Ok(contents) Ok(contents)
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug)] pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
pub(crate) enum CfgExpr { let item = syn::parse2::<Item>(quote! { #[hdl(#attr)] #item })?;
Option {
ident: Ident,
value: Option<(Token![=], LitStr)>,
},
All {
all: kw::all,
paren: Paren,
exprs: Punctuated<CfgExpr, Token![,]>,
},
Any {
any: kw::any,
paren: Paren,
exprs: Punctuated<CfgExpr, Token![,]>,
},
Not {
not: kw::not,
paren: Paren,
expr: Box<CfgExpr>,
trailing_comma: Option<Token![,]>,
},
}
impl Parse for CfgExpr {
fn parse(input: ParseStream) -> syn::Result<Self> {
match input.cursor().ident() {
Some((_, cursor)) if cursor.eof() => {
return Ok(CfgExpr::Option {
ident: input.call(Ident::parse_any)?,
value: None,
});
}
_ => {}
}
if input.peek(Ident::peek_any) && input.peek2(Token![=]) {
return Ok(CfgExpr::Option {
ident: input.call(Ident::parse_any)?,
value: Some((input.parse()?, input.parse()?)),
});
}
let contents;
if input.peek(kw::all) {
Ok(CfgExpr::All {
all: input.parse()?,
paren: parenthesized!(contents in input),
exprs: contents.call(Punctuated::parse_terminated)?,
})
} else if input.peek(kw::any) {
Ok(CfgExpr::Any {
any: input.parse()?,
paren: parenthesized!(contents in input),
exprs: contents.call(Punctuated::parse_terminated)?,
})
} else if input.peek(kw::not) {
Ok(CfgExpr::Not {
not: input.parse()?,
paren: parenthesized!(contents in input),
expr: contents.parse()?,
trailing_comma: contents.parse()?,
})
} else {
Err(input.error("expected cfg-pattern"))
}
}
}
impl ToTokens for CfgExpr {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
CfgExpr::Option { ident, value } => {
ident.to_tokens(tokens);
if let Some((eq, value)) = value {
eq.to_tokens(tokens);
value.to_tokens(tokens);
}
}
CfgExpr::All { all, paren, exprs } => {
all.to_tokens(tokens);
paren.surround(tokens, |tokens| exprs.to_tokens(tokens));
}
CfgExpr::Any { any, paren, exprs } => {
any.to_tokens(tokens);
paren.surround(tokens, |tokens| exprs.to_tokens(tokens));
}
CfgExpr::Not {
not,
paren,
expr,
trailing_comma,
} => {
not.to_tokens(tokens);
paren.surround(tokens, |tokens| {
expr.to_tokens(tokens);
trailing_comma.to_tokens(tokens);
});
}
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct Cfg {
cfg: kw::cfg,
paren: Paren,
expr: CfgExpr,
trailing_comma: Option<Token![,]>,
}
impl Cfg {
fn parse_meta(meta: &Meta) -> syn::Result<Self> {
syn::parse2(meta.to_token_stream())
}
}
impl ToTokens for Cfg {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
cfg,
paren,
expr,
trailing_comma,
} = self;
cfg.to_tokens(tokens);
paren.surround(tokens, |tokens| {
expr.to_tokens(tokens);
trailing_comma.to_tokens(tokens);
});
}
}
impl Parse for Cfg {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contents;
Ok(Self {
cfg: input.parse()?,
paren: parenthesized!(contents in input),
expr: contents.parse()?,
trailing_comma: contents.parse()?,
})
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct CfgAttr {
cfg_attr: kw::cfg_attr,
paren: Paren,
expr: CfgExpr,
comma: Token![,],
attrs: Punctuated<Meta, Token![,]>,
}
impl CfgAttr {
pub(crate) fn to_cfg(&self) -> Cfg {
Cfg {
cfg: kw::cfg(self.cfg_attr.span),
paren: self.paren,
expr: self.expr.clone(),
trailing_comma: None,
}
}
fn parse_meta(meta: &Meta) -> syn::Result<Self> {
syn::parse2(meta.to_token_stream())
}
}
impl Parse for CfgAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contents;
Ok(Self {
cfg_attr: input.parse()?,
paren: parenthesized!(contents in input),
expr: contents.parse()?,
comma: contents.parse()?,
attrs: contents.call(Punctuated::parse_terminated)?,
})
}
}
pub(crate) struct CfgAndValue {
cfg: Cfg,
eq_token: Token![=],
value: LitBool,
}
impl Parse for CfgAndValue {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
cfg: input.parse()?,
eq_token: input.parse()?,
value: input.parse()?,
})
}
}
pub(crate) struct Cfgs<T> {
pub(crate) bracket: Bracket,
pub(crate) cfgs_map: HashMap<Cfg, T>,
pub(crate) cfgs_list: Vec<Cfg>,
}
impl<T> Default for Cfgs<T> {
fn default() -> Self {
Self {
bracket: Default::default(),
cfgs_map: Default::default(),
cfgs_list: Default::default(),
}
}
}
impl<T> Cfgs<T> {
fn insert_cfg(&mut self, cfg: Cfg, value: T) {
match self.cfgs_map.entry(cfg) {
Entry::Occupied(_) => {}
Entry::Vacant(entry) => {
self.cfgs_list.push(entry.key().clone());
entry.insert(value);
}
}
}
}
impl Parse for Cfgs<bool> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contents;
let bracket = bracketed!(contents in input);
let mut cfgs_map = HashMap::new();
let mut cfgs_list = Vec::new();
for CfgAndValue {
cfg,
eq_token,
value,
} in contents.call(Punctuated::<CfgAndValue, Token![,]>::parse_terminated)?
{
let _ = eq_token;
match cfgs_map.entry(cfg) {
Entry::Occupied(_) => {}
Entry::Vacant(entry) => {
cfgs_list.push(entry.key().clone());
entry.insert(value.value);
}
}
}
Ok(Self {
bracket,
cfgs_map,
cfgs_list,
})
}
}
impl Parse for Cfgs<()> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contents;
let bracket = bracketed!(contents in input);
let mut cfgs_map = HashMap::new();
let mut cfgs_list = Vec::new();
for cfg in contents.call(Punctuated::<Cfg, Token![,]>::parse_terminated)? {
match cfgs_map.entry(cfg) {
Entry::Occupied(_) => {}
Entry::Vacant(entry) => {
cfgs_list.push(entry.key().clone());
entry.insert(());
}
}
}
Ok(Self {
bracket,
cfgs_map,
cfgs_list,
})
}
}
impl ToTokens for Cfgs<()> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
bracket,
cfgs_map: _,
cfgs_list,
} = self;
bracket.surround(tokens, |tokens| {
for cfg in cfgs_list {
cfg.to_tokens(tokens);
<Token![,]>::default().to_tokens(tokens);
}
});
}
}
fn hdl_main(
kw: impl CustomToken,
attr: TokenStream,
item: TokenStream,
) -> syn::Result<TokenStream> {
fn parse_evaluated_cfgs_attr<R>(
input: ParseStream,
parse_inner: impl FnOnce(ParseStream) -> syn::Result<R>,
) -> syn::Result<R> {
let _: Token![#] = input.parse()?;
let bracket_content;
bracketed!(bracket_content in input);
let _: kw::__evaluated_cfgs = bracket_content.parse()?;
let paren_content;
parenthesized!(paren_content in bracket_content);
parse_inner(&paren_content)
}
let (evaluated_cfgs, item): (_, TokenStream) = Parser::parse2(
|input: ParseStream| {
let peek = input.fork();
if parse_evaluated_cfgs_attr(&peek, |_| Ok(())).is_ok() {
let evaluated_cfgs = parse_evaluated_cfgs_attr(input, Cfgs::<bool>::parse)?;
Ok((Some(evaluated_cfgs), input.parse()?))
} else {
Ok((None, input.parse()?))
}
},
item,
)?;
let cfgs = if let Some(cfgs) = evaluated_cfgs {
cfgs
} else {
let cfgs = process_cfg::collect_cfgs(syn::parse2(item.clone())?)?;
if cfgs.cfgs_list.is_empty() {
Cfgs::default()
} else {
return Ok(quote! {
::fayalite::__cfg_expansion_helper! {
[]
#cfgs
{#[::fayalite::#kw(#attr)]} { #item }
}
});
}
};
let item = syn::parse2(quote! { #[#kw(#attr)] #item })?;
let Some(item) = process_cfg::process_cfgs(item, cfgs)? else {
return Ok(TokenStream::new());
};
match item { match item {
Item::Enum(item) => hdl_enum::hdl_enum(item), Item::Enum(item) => hdl_enum::hdl_enum(item),
Item::Struct(item) => hdl_bundle::hdl_bundle(item), Item::Struct(item) => hdl_bundle::hdl_bundle(item),
Item::Fn(item) => hdl_module_impl(item),
Item::Type(item) => hdl_type_alias_impl(item),
_ => Err(syn::Error::new( _ => Err(syn::Error::new(
Span::call_site(), Span::call_site(),
"top-level #[hdl] can only be used on structs, enums, type aliases, or functions", "top-level #[hdl] can only be used on structs or enums",
)), )),
} }
} }
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
hdl_main(kw::hdl_module::default(), attr, item)
}
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
hdl_main(kw::hdl::default(), attr, item)
}

View file

@ -2,7 +2,6 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
hdl_type_common::{ParsedGenerics, SplitForImpl}, hdl_type_common::{ParsedGenerics, SplitForImpl},
kw,
module::transform_body::{HdlLet, HdlLetKindIO}, module::transform_body::{HdlLet, HdlLetKindIO},
options, Errors, HdlAttr, PairsIterExt, options, Errors, HdlAttr, PairsIterExt,
}; };
@ -10,6 +9,7 @@ use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned, ToTokens}; use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::collections::HashSet; use std::collections::HashSet;
use syn::{ use syn::{
parse::{Parse, ParseStream},
parse_quote, parse_quote,
visit::{visit_pat, Visit}, visit::{visit_pat, Visit},
Attribute, Block, ConstParam, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct, Attribute, Block, ConstParam, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct,
@ -59,9 +59,9 @@ impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> {
pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>; pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>;
struct ModuleFnModule { pub(crate) struct ModuleFn {
attrs: Vec<Attribute>, attrs: Vec<Attribute>,
config_options: HdlAttr<ConfigOptions, kw::hdl_module>, config_options: HdlAttr<ConfigOptions>,
module_kind: ModuleKind, module_kind: ModuleKind,
vis: Visibility, vis: Visibility,
sig: Signature, sig: Signature,
@ -70,26 +70,6 @@ struct ModuleFnModule {
the_struct: TokenStream, the_struct: TokenStream,
} }
enum ModuleFnImpl {
Module(ModuleFnModule),
Fn {
attrs: Vec<Attribute>,
config_options: HdlAttr<ConfigOptions, kw::hdl>,
vis: Visibility,
sig: Signature,
block: Box<Block>,
},
}
options! {
pub(crate) enum HdlOrHdlModule {
Hdl(hdl),
HdlModule(hdl_module),
}
}
pub(crate) struct ModuleFn(ModuleFnImpl);
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub(crate) enum ModuleKind { pub(crate) enum ModuleKind {
Extern, Extern,
@ -109,25 +89,14 @@ impl Visit<'_> for ContainsSkippedIdent<'_> {
} }
} }
impl ModuleFn { impl Parse for ModuleFn {
pub(crate) fn config_options(&self) -> ConfigOptions { fn parse(input: ParseStream) -> syn::Result<Self> {
let (ModuleFnImpl::Module(ModuleFnModule {
config_options: HdlAttr { body, .. },
..
})
| ModuleFnImpl::Fn {
config_options: HdlAttr { body, .. },
..
}) = &self.0;
body.clone()
}
pub(crate) fn parse_from_fn(item: ItemFn) -> syn::Result<Self> {
let ItemFn { let ItemFn {
mut attrs, mut attrs,
vis, vis,
mut sig, mut sig,
block, block,
} = item; } = input.parse()?;
let Signature { let Signature {
ref constness, ref constness,
ref asyncness, ref asyncness,
@ -142,60 +111,43 @@ impl ModuleFn {
ref output, ref output,
} = sig; } = sig;
let mut errors = Errors::new(); let mut errors = Errors::new();
let Some(mut config_options) = let config_options = errors
errors.unwrap_or_default( .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
HdlAttr::<ConfigOptions, HdlOrHdlModule>::parse_and_take_attr(&mut attrs), .unwrap_or_default();
)
else {
errors.error(sig.ident, "missing #[hdl] or #[hdl_module] attribute");
errors.finish()?;
unreachable!();
};
let ConfigOptions { let ConfigOptions {
outline_generated: _, outline_generated: _,
extern_, extern_,
} = config_options.body; } = config_options.body;
let module_kind = match (config_options.kw, extern_) { let module_kind = match extern_ {
(HdlOrHdlModule::Hdl(_), None) => None, Some(_) => ModuleKind::Extern,
(HdlOrHdlModule::Hdl(_), Some(extern2)) => { None => ModuleKind::Normal,
config_options.body.extern_ = None;
errors.error(
extern2.0,
"extern can only be used as #[hdl_module(extern)]",
);
None
}
(HdlOrHdlModule::HdlModule(_), None) => Some(ModuleKind::Normal),
(HdlOrHdlModule::HdlModule(_), Some(_)) => Some(ModuleKind::Extern),
}; };
if let HdlOrHdlModule::HdlModule(_) = config_options.kw { for fn_arg in inputs {
for fn_arg in inputs { match fn_arg {
match fn_arg { FnArg::Receiver(_) => {
FnArg::Receiver(_) => { errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here"));
errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here")); }
} FnArg::Typed(fn_arg) => {
FnArg::Typed(fn_arg) => { visit_pat(
visit_pat( &mut CheckNameConflictsWithModuleBuilderVisitor {
&mut CheckNameConflictsWithModuleBuilderVisitor { errors: &mut errors,
errors: &mut errors, },
}, &fn_arg.pat,
&fn_arg.pat, );
);
}
} }
} }
if let Some(constness) = constness { }
errors.push(syn::Error::new_spanned(constness, "const not allowed here")); if let Some(constness) = constness {
} errors.push(syn::Error::new_spanned(constness, "const not allowed here"));
if let Some(asyncness) = asyncness { }
errors.push(syn::Error::new_spanned(asyncness, "async not allowed here")); if let Some(asyncness) = asyncness {
} errors.push(syn::Error::new_spanned(asyncness, "async not allowed here"));
if let Some(unsafety) = unsafety { }
errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here")); if let Some(unsafety) = unsafety {
} errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here"));
if let Some(abi) = abi { }
errors.push(syn::Error::new_spanned(abi, "extern not allowed here")); if let Some(abi) = abi {
} errors.push(syn::Error::new_spanned(abi, "extern not allowed here"));
} }
let mut skipped_idents = HashSet::new(); let mut skipped_idents = HashSet::new();
let struct_generic_params = generics let struct_generic_params = generics
@ -203,17 +155,14 @@ impl ModuleFn {
.pairs_mut() .pairs_mut()
.filter_map_pair_value_mut(|v| match v { .filter_map_pair_value_mut(|v| match v {
GenericParam::Lifetime(LifetimeParam { attrs, .. }) => { GenericParam::Lifetime(LifetimeParam { attrs, .. }) => {
errors.unwrap_or_default( errors
HdlAttr::<crate::kw::skip, kw::hdl>::parse_and_take_attr(attrs), .unwrap_or_default(HdlAttr::<crate::kw::skip>::parse_and_take_attr(attrs));
);
None None
} }
GenericParam::Type(TypeParam { attrs, ident, .. }) GenericParam::Type(TypeParam { attrs, ident, .. })
| GenericParam::Const(ConstParam { attrs, ident, .. }) => { | GenericParam::Const(ConstParam { attrs, ident, .. }) => {
if errors if errors
.unwrap_or_default( .unwrap_or_default(HdlAttr::<crate::kw::skip>::parse_and_take_attr(attrs))
HdlAttr::<crate::kw::skip, kw::hdl>::parse_and_take_attr(attrs),
)
.is_some() .is_some()
{ {
skipped_idents.insert(ident.clone()); skipped_idents.insert(ident.clone());
@ -227,7 +176,6 @@ impl ModuleFn {
let struct_where_clause = generics let struct_where_clause = generics
.where_clause .where_clause
.as_mut() .as_mut()
.filter(|_| matches!(config_options.kw, HdlOrHdlModule::HdlModule(_)))
.map(|where_clause| WhereClause { .map(|where_clause| WhereClause {
where_token: where_clause.where_token, where_token: where_clause.where_token,
predicates: where_clause predicates: where_clause
@ -250,26 +198,22 @@ impl ModuleFn {
}) })
.collect(), .collect(),
}); });
let struct_generics = if let HdlOrHdlModule::HdlModule(_) = config_options.kw { let struct_generics = Generics {
let mut struct_generics = Generics { lt_token: generics.lt_token,
lt_token: generics.lt_token, params: struct_generic_params,
params: struct_generic_params, gt_token: generics.gt_token,
gt_token: generics.gt_token, where_clause: struct_where_clause,
where_clause: struct_where_clause,
};
if let Some(variadic) = variadic {
errors.push(syn::Error::new_spanned(variadic, "... not allowed here"));
}
if !matches!(output, ReturnType::Default) {
errors.push(syn::Error::new_spanned(
output,
"return type not allowed here",
));
}
errors.ok(ParsedGenerics::parse(&mut struct_generics))
} else {
Some(ParsedGenerics::default())
}; };
if let Some(variadic) = variadic {
errors.push(syn::Error::new_spanned(variadic, "... not allowed here"));
}
if !matches!(output, ReturnType::Default) {
errors.push(syn::Error::new_spanned(
output,
"return type not allowed here",
));
}
let struct_generics = errors.ok(ParsedGenerics::parse(&mut { struct_generics }));
let body_results = struct_generics.as_ref().and_then(|struct_generics| { let body_results = struct_generics.as_ref().and_then(|struct_generics| {
errors.ok(transform_body::transform_body( errors.ok(transform_body::transform_body(
module_kind, module_kind,
@ -280,47 +224,6 @@ impl ModuleFn {
errors.finish()?; errors.finish()?;
let struct_generics = struct_generics.unwrap(); let struct_generics = struct_generics.unwrap();
let (block, io) = body_results.unwrap(); let (block, io) = body_results.unwrap();
let config_options = match config_options {
HdlAttr {
pound_token,
style,
bracket_token,
kw: HdlOrHdlModule::Hdl((kw,)),
paren_token,
body,
} => {
debug_assert!(io.is_empty());
return Ok(Self(ModuleFnImpl::Fn {
attrs,
config_options: HdlAttr {
pound_token,
style,
bracket_token,
kw,
paren_token,
body,
},
vis,
sig,
block,
}));
}
HdlAttr {
pound_token,
style,
bracket_token,
kw: HdlOrHdlModule::HdlModule((kw,)),
paren_token,
body,
} => HdlAttr {
pound_token,
style,
bracket_token,
kw,
paren_token,
body,
},
};
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 };
@ -356,22 +259,7 @@ impl ModuleFn {
} }
}; };
let the_struct = crate::hdl_bundle::hdl_bundle(the_struct)?; let the_struct = crate::hdl_bundle::hdl_bundle(the_struct)?;
Ok(Self(ModuleFnImpl::Module(ModuleFnModule { Ok(Self {
attrs,
config_options,
module_kind: module_kind.unwrap(),
vis,
sig,
block,
struct_generics,
the_struct,
})))
}
}
impl ModuleFn {
pub(crate) fn generate(self) -> TokenStream {
let ModuleFnModule {
attrs, attrs,
config_options, config_options,
module_kind, module_kind,
@ -380,28 +268,22 @@ impl ModuleFn {
block, block,
struct_generics, struct_generics,
the_struct, the_struct,
} = match self.0 { })
ModuleFnImpl::Module(v) => v, }
ModuleFnImpl::Fn { }
attrs,
config_options, impl ModuleFn {
vis, pub(crate) fn generate(self) -> TokenStream {
sig, let Self {
block, attrs,
} => { config_options,
let ConfigOptions { module_kind,
outline_generated: _, vis,
extern_: _, sig,
} = config_options.body; block,
return ItemFn { struct_generics,
attrs, the_struct,
vis, } = self;
sig,
block,
}
.into_token_stream();
}
};
let ConfigOptions { let ConfigOptions {
outline_generated: _, outline_generated: _,
extern_: _, extern_: _,
@ -450,21 +332,12 @@ impl ModuleFn {
let fn_name_str = fn_name.to_string(); let fn_name_str = fn_name.to_string();
let (_, body_type_generics, _) = body_fn.sig.generics.split_for_impl(); let (_, body_type_generics, _) = body_fn.sig.generics.split_for_impl();
let body_turbofish_type_generics = body_type_generics.as_turbofish(); let body_turbofish_type_generics = body_type_generics.as_turbofish();
let body_lambda = if param_names.is_empty() {
quote! {
__body #body_turbofish_type_generics
}
} else {
quote! {
|m| __body #body_turbofish_type_generics(m, #(#param_names,)*)
}
};
let block = parse_quote! {{ let block = parse_quote! {{
#body_fn #body_fn
::fayalite::module::ModuleBuilder::run( ::fayalite::module::ModuleBuilder::run(
#fn_name_str, #fn_name_str,
#module_kind_value, #module_kind_value,
#body_lambda, |m| __body #body_turbofish_type_generics(m, #(#param_names,)*),
) )
}}; }};
let outer_fn = ItemFn { let outer_fn = ItemFn {

View file

@ -34,7 +34,6 @@ options! {
Instance(instance), Instance(instance),
RegBuilder(reg_builder), RegBuilder(reg_builder),
Wire(wire), Wire(wire),
IncompleteWire(incomplete_wire),
Memory(memory), Memory(memory),
MemoryArray(memory_array), MemoryArray(memory_array),
MemoryWithInit(memory_with_init), MemoryWithInit(memory_with_init),
@ -265,6 +264,11 @@ pub(crate) enum RegBuilderReset {
paren: Paren, paren: Paren,
init_expr: Box<Expr>, init_expr: Box<Expr>,
}, },
ResetDefault {
dot_token: Token![.],
reset_default: kw::reset_default,
paren: Paren,
},
} }
impl_fold! { impl_fold! {
@ -281,6 +285,11 @@ impl_fold! {
paren: Paren, paren: Paren,
init_expr: Box<Expr>, init_expr: Box<Expr>,
}, },
ResetDefault {
dot_token: Token![.],
reset_default: kw::reset_default,
paren: Paren,
},
} }
} }
@ -302,6 +311,11 @@ impl Parse for RegBuilderReset {
paren: parenthesized!(paren_contents in input), paren: parenthesized!(paren_contents in input),
init_expr: paren_contents.call(parse_single_fn_arg)?, init_expr: paren_contents.call(parse_single_fn_arg)?,
}), }),
RegBuilderMethod::ResetDefault(reset_default) => Ok(Self::ResetDefault {
dot_token,
reset_default,
paren: parenthesized!(paren_contents in input),
}),
} }
} }
} }
@ -329,6 +343,15 @@ impl ToTokens for RegBuilderReset {
reset.to_tokens(tokens); reset.to_tokens(tokens);
paren.surround(tokens, |tokens| init_expr.to_tokens(tokens)); paren.surround(tokens, |tokens| init_expr.to_tokens(tokens));
} }
RegBuilderReset::ResetDefault {
dot_token,
reset_default,
paren,
} => {
dot_token.to_tokens(tokens);
reset_default.to_tokens(tokens);
paren.surround(tokens, |_| {});
}
} }
} }
} }
@ -377,6 +400,8 @@ make_builder_method_enum! {
NoReset(no_reset), NoReset(no_reset),
#[cond = need_reset] #[cond = need_reset]
Reset(reset), Reset(reset),
#[cond = need_reset]
ResetDefault(reset_default),
} }
} }
@ -419,13 +444,17 @@ impl HdlLetKindRegBuilder {
let mut clock_domain = None; let mut clock_domain = None;
match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, true)?.1 { match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, true)?.1 {
RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?), RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?),
RegBuilderMethod::NoReset(_) | RegBuilderMethod::Reset(_) => {} RegBuilderMethod::NoReset(_)
| RegBuilderMethod::Reset(_)
| RegBuilderMethod::ResetDefault(_) => {}
} }
let reset = input.parse()?; let reset = input.parse()?;
if clock_domain.is_none() { if clock_domain.is_none() {
match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, false)?.1 { match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, false)?.1 {
RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?), RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?),
RegBuilderMethod::NoReset(_) | RegBuilderMethod::Reset(_) => unreachable!(), RegBuilderMethod::NoReset(_)
| RegBuilderMethod::Reset(_)
| RegBuilderMethod::ResetDefault(_) => unreachable!(),
} }
} }
Ok(Self { Ok(Self {
@ -504,41 +533,6 @@ impl HdlLetKindToTokens for HdlLetKindWire {
} }
} }
options! {
pub(crate) enum LetFnKindIncomplete {
IncompleteWire(incomplete_wire),
}
}
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindIncomplete {
pub(crate) kind: LetFnKindIncomplete,
pub(crate) paren: Paren,
}
impl ParseTypes<Self> for HdlLetKindIncomplete {
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
Ok(input.clone())
}
}
impl_fold! {
struct HdlLetKindIncomplete<> {
kind: LetFnKindIncomplete,
paren: Paren,
}
}
impl HdlLetKindToTokens for HdlLetKindIncomplete {
fn ty_to_tokens(&self, _tokens: &mut TokenStream) {}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self { kind, paren } = self;
kind.to_tokens(tokens);
paren.surround(tokens, |_| {});
}
}
options! { options! {
pub(crate) enum MemoryFnName { pub(crate) enum MemoryFnName {
Memory(memory), Memory(memory),
@ -703,7 +697,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>),
Incomplete(HdlLetKindIncomplete),
Instance(HdlLetKindInstance), Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder), RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire), Wire(HdlLetKindWire),
@ -713,7 +706,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>),
Incomplete(HdlLetKindIncomplete),
Instance(HdlLetKindInstance), Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder), RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire), Wire(HdlLetKindWire),
@ -728,9 +720,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::Incomplete(input) => {
ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete)
}
HdlLetKind::Instance(input) => { HdlLetKind::Instance(input) => {
ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance) ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance)
} }
@ -882,20 +871,6 @@ impl HdlLetKindParse for HdlLetKind<Type> {
ty_expr: paren_contents.call(parse_optional_fn_arg)?, ty_expr: paren_contents.call(parse_optional_fn_arg)?,
})) }))
} }
LetFnKind::IncompleteWire(incomplete_wire) => {
if let Some(parsed_ty) = parsed_ty {
return Err(Error::new_spanned(
parsed_ty.1,
"type annotation not allowed for incomplete_wire",
));
}
check_empty_m_dot(m_dot, kind)?;
let _paren_contents;
Ok(Self::Incomplete(HdlLetKindIncomplete {
kind: LetFnKindIncomplete::IncompleteWire(incomplete_wire),
paren: parenthesized!(_paren_contents in input),
}))
}
LetFnKind::Memory(fn_name) => HdlLetKindMemory::rest_of_parse( LetFnKind::Memory(fn_name) => HdlLetKindMemory::rest_of_parse(
input, input,
parsed_ty, parsed_ty,
@ -928,7 +903,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::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),
HdlLetKind::Wire(v) => v.ty_to_tokens(tokens), HdlLetKind::Wire(v) => v.ty_to_tokens(tokens),
@ -939,7 +913,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::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),
HdlLetKind::Wire(v) => v.expr_to_tokens(tokens), HdlLetKind::Wire(v) => v.expr_to_tokens(tokens),
@ -952,7 +925,7 @@ with_debug_clone_and_fold! {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) struct HdlLet<Kind = HdlLetKind> { pub(crate) struct HdlLet<Kind = HdlLetKind> {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) hdl_attr: HdlAttr<Nothing, kw::hdl>, pub(crate) hdl_attr: HdlAttr<Nothing>,
pub(crate) let_token: Token![let], pub(crate) let_token: Token![let],
pub(crate) mut_token: Option<Token![mut]>, pub(crate) mut_token: Option<Token![mut]>,
pub(crate) name: Ident, pub(crate) name: Ident,
@ -1109,7 +1082,7 @@ fn parse_quote_let_pat<T, R: ToTokens, C: Borrow<Token![:]>>(
} }
} }
pub(crate) fn wrap_ty_with_expr(ty: impl ToTokens) -> Type { fn wrap_ty_with_expr(ty: impl ToTokens) -> Type {
parse_quote_spanned! {ty.span()=> parse_quote_spanned! {ty.span()=>
::fayalite::expr::Expr<#ty> ::fayalite::expr::Expr<#ty>
} }
@ -1139,7 +1112,7 @@ impl<T: ToString> ToTokens for ImplicitName<T> {
} }
struct Visitor<'a> { struct Visitor<'a> {
module_kind: Option<ModuleKind>, module_kind: ModuleKind,
errors: Errors, errors: Errors,
io: Vec<ModuleIO>, io: Vec<ModuleIO>,
block_depth: usize, block_depth: usize,
@ -1147,33 +1120,22 @@ struct Visitor<'a> {
} }
impl Visitor<'_> { impl Visitor<'_> {
fn take_hdl_attr<T: Parse>( fn take_hdl_attr<T: Parse>(&mut self, attrs: &mut Vec<Attribute>) -> Option<HdlAttr<T>> {
&mut self,
attrs: &mut Vec<Attribute>,
) -> Option<HdlAttr<T, kw::hdl>> {
self.errors.unwrap_or( self.errors.unwrap_or(
HdlAttr::parse_and_take_attr(attrs), HdlAttr::parse_and_take_attr(attrs),
Some(syn::parse2::<T>(quote! {}).unwrap().into()), Some(syn::parse2::<T>(quote! {}).unwrap().into()),
) )
} }
fn require_normal_module_or_fn(&mut self, spanned: impl ToTokens) { fn require_normal_module(&mut self, spanned: impl ToTokens) {
match self.module_kind { match self.module_kind {
Some(ModuleKind::Extern) => { ModuleKind::Extern => {
self.errors self.errors
.error(spanned, "not allowed in #[hdl_module(extern)]"); .error(spanned, "not allowed in #[hdl_module(extern)]");
} }
Some(ModuleKind::Normal) | None => {} ModuleKind::Normal => {}
} }
} }
fn require_module(&mut self, spanned: impl ToTokens) { fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing>, expr_if: ExprIf) -> Expr {
match self.module_kind {
None => {
self.errors.error(spanned, "not allowed in #[hdl] fn");
}
Some(_) => {}
}
}
fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing, kw::hdl>, expr_if: ExprIf) -> Expr {
let ExprIf { let ExprIf {
attrs, attrs,
if_token, if_token,
@ -1181,7 +1143,7 @@ impl Visitor<'_> {
then_branch, then_branch,
else_branch, else_branch,
} = expr_if; } = expr_if;
self.require_normal_module_or_fn(if_token); self.require_normal_module(if_token);
let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr { let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr {
Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if), Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if),
expr => expr, expr => expr,
@ -1246,12 +1208,11 @@ impl Visitor<'_> {
.to_tokens(expr); .to_tokens(expr);
}); });
let mut attrs = hdl_let.attrs.clone(); let mut attrs = hdl_let.attrs.clone();
self.require_module(kind);
match self.module_kind { match self.module_kind {
Some(ModuleKind::Extern) => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=> ModuleKind::Extern => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=>
#[allow(unused_variables)] #[allow(unused_variables)]
}), }),
Some(ModuleKind::Normal) | None => {} ModuleKind::Normal => {}
} }
let let_stmt = Local { let let_stmt = Local {
attrs, attrs,
@ -1288,7 +1249,7 @@ impl Visitor<'_> {
}, },
semi_token, semi_token,
} = hdl_let; } = hdl_let;
self.require_normal_module_or_fn(instance); self.require_normal_module(instance);
let mut expr = instance.to_token_stream(); let mut expr = instance.to_token_stream();
paren.surround(&mut expr, |expr| { paren.surround(&mut expr, |expr| {
let name_str = ImplicitName { let name_str = ImplicitName {
@ -1315,7 +1276,7 @@ impl Visitor<'_> {
fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local { fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local {
let name = &hdl_let.name; let name = &hdl_let.name;
let reg_builder = hdl_let.kind.reg_builder; let reg_builder = hdl_let.kind.reg_builder;
self.require_normal_module_or_fn(reg_builder); self.require_normal_module(reg_builder);
let mut expr = reg_builder.to_token_stream(); let mut expr = reg_builder.to_token_stream();
hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| { hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| {
let name_str = ImplicitName { let name_str = ImplicitName {
@ -1340,7 +1301,7 @@ impl Visitor<'_> {
no_reset.to_tokens(&mut expr); no_reset.to_tokens(&mut expr);
paren.surround(&mut expr, |expr| ty_expr.to_tokens(expr)); paren.surround(&mut expr, |expr| ty_expr.to_tokens(expr));
} }
RegBuilderReset::Reset { .. } => { RegBuilderReset::Reset { .. } | RegBuilderReset::ResetDefault { .. } => {
hdl_let.kind.reset.to_tokens(&mut expr); hdl_let.kind.reset.to_tokens(&mut expr);
} }
} }
@ -1366,7 +1327,7 @@ impl Visitor<'_> {
fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local { fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local {
let name = &hdl_let.name; let name = &hdl_let.name;
let wire = hdl_let.kind.wire; let wire = hdl_let.kind.wire;
self.require_normal_module_or_fn(wire); self.require_normal_module(wire);
let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span()); let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span());
let mut expr = wire.to_token_stream(); let mut expr = wire.to_token_stream();
hdl_let.kind.paren.surround(&mut expr, |expr| { hdl_let.kind.paren.surround(&mut expr, |expr| {
@ -1396,36 +1357,11 @@ impl Visitor<'_> {
semi_token: hdl_let.semi_token, semi_token: hdl_let.semi_token,
} }
} }
fn process_hdl_let_incomplete(&mut self, hdl_let: HdlLet<HdlLetKindIncomplete>) -> Local {
let name = &hdl_let.name;
let kind = hdl_let.kind.kind;
self.require_normal_module_or_fn(kind);
let mut expr = kind.to_token_stream();
hdl_let.kind.paren.surround(&mut expr, |expr| {
ImplicitName {
name,
span: name.span(),
}
.to_tokens(expr);
});
let mut_token = &hdl_let.mut_token;
Local {
attrs: hdl_let.attrs.clone(),
let_token: hdl_let.let_token,
pat: parse_quote! { #mut_token #name },
init: Some(LocalInit {
eq_token: hdl_let.eq_token,
expr: parse_quote! { #expr },
diverge: None,
}),
semi_token: hdl_let.semi_token,
}
}
fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local { fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local {
let name = &hdl_let.name; let name = &hdl_let.name;
let memory_fn = hdl_let.kind.memory_fn; let memory_fn = hdl_let.kind.memory_fn;
let memory_fn_name = memory_fn.name(); let memory_fn_name = memory_fn.name();
self.require_normal_module_or_fn(memory_fn_name); self.require_normal_module(memory_fn_name);
let mut expr = memory_fn_name.to_token_stream(); let mut expr = memory_fn_name.to_token_stream();
let (paren, arg) = match memory_fn { let (paren, arg) = match memory_fn {
MemoryFn::Memory { MemoryFn::Memory {
@ -1490,7 +1426,6 @@ impl Visitor<'_> {
} }
the_match! { the_match! {
IO => process_hdl_let_io, IO => process_hdl_let_io,
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,
Wire => process_hdl_let_wire, Wire => process_hdl_let_wire,
@ -1586,7 +1521,7 @@ impl Visitor<'_> {
} }
} }
pub(crate) fn empty_let() -> Local { fn empty_let() -> Local {
Local { Local {
attrs: vec![], attrs: vec![],
let_token: Default::default(), let_token: Default::default(),
@ -1608,7 +1543,7 @@ impl Fold for Visitor<'_> {
} }
fn fold_attribute(&mut self, attr: Attribute) -> Attribute { fn fold_attribute(&mut self, attr: Attribute) -> Attribute {
if is_hdl_attr::<kw::hdl>(&attr) { if is_hdl_attr(&attr) {
self.errors self.errors
.error(&attr, "#[hdl] attribute not supported here"); .error(&attr, "#[hdl] attribute not supported here");
} }
@ -1672,35 +1607,15 @@ impl Fold for Visitor<'_> {
} }
} }
fn fold_local(&mut self, mut let_stmt: Local) -> Local { fn fold_local(&mut self, let_stmt: Local) -> Local {
match self match self
.errors .errors
.ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr( .ok(HdlAttr::<Nothing>::parse_and_leave_attr(&let_stmt.attrs))
&let_stmt.attrs, {
)) {
None => return empty_let(), None => return empty_let(),
Some(None) => return fold_local(self, let_stmt), Some(None) => return fold_local(self, let_stmt),
Some(Some(HdlAttr { .. })) => {} Some(Some(HdlAttr { .. })) => {}
}; };
let mut pat = &let_stmt.pat;
if let Pat::Type(pat_type) = pat {
pat = &pat_type.pat;
}
let Pat::Ident(syn::PatIdent {
attrs: _,
by_ref: None,
mutability: _,
ident: _,
subpat: None,
}) = pat
else {
let hdl_attr = HdlAttr::<Nothing, kw::hdl>::parse_and_take_attr(&mut let_stmt.attrs)
.ok()
.flatten()
.expect("already checked above");
let let_stmt = fold_local(self, let_stmt);
return self.process_hdl_let_pat(hdl_attr, let_stmt);
};
let hdl_let = syn::parse2::<HdlLet<HdlLetKind<Type>>>(let_stmt.into_token_stream()); let hdl_let = syn::parse2::<HdlLet<HdlLetKind<Type>>>(let_stmt.into_token_stream());
let Some(hdl_let) = self.errors.ok(hdl_let) else { let Some(hdl_let) = self.errors.ok(hdl_let) else {
return empty_let(); return empty_let();
@ -1731,7 +1646,7 @@ impl Fold for Visitor<'_> {
} }
pub(crate) fn transform_body( pub(crate) fn transform_body(
module_kind: Option<ModuleKind>, module_kind: ModuleKind,
mut body: Box<Block>, mut body: Box<Block>,
parsed_generics: &ParsedGenerics, parsed_generics: &ParsedGenerics,
) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> { ) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {

View file

@ -1,6 +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
use crate::{kw, module::transform_body::Visitor, HdlAttr}; use crate::{module::transform_body::Visitor, HdlAttr};
use quote::{format_ident, quote_spanned}; use quote::{format_ident, quote_spanned};
use syn::{ use syn::{
parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath,
@ -10,10 +10,10 @@ use syn::{
impl Visitor<'_> { impl Visitor<'_> {
pub(crate) fn process_hdl_array( pub(crate) fn process_hdl_array(
&mut self, &mut self,
hdl_attr: HdlAttr<Nothing, kw::hdl>, hdl_attr: HdlAttr<Nothing>,
mut expr_array: ExprArray, mut expr_array: ExprArray,
) -> Expr { ) -> Expr {
self.require_normal_module_or_fn(hdl_attr); self.require_normal_module(hdl_attr);
for elem in &mut expr_array.elems { for elem in &mut expr_array.elems {
*elem = parse_quote_spanned! {elem.span()=> *elem = parse_quote_spanned! {elem.span()=>
::fayalite::expr::ToExpr::to_expr(&(#elem)) ::fayalite::expr::ToExpr::to_expr(&(#elem))
@ -23,10 +23,10 @@ impl Visitor<'_> {
} }
pub(crate) fn process_hdl_repeat( pub(crate) fn process_hdl_repeat(
&mut self, &mut self,
hdl_attr: HdlAttr<Nothing, kw::hdl>, hdl_attr: HdlAttr<Nothing>,
mut expr_repeat: ExprRepeat, mut expr_repeat: ExprRepeat,
) -> Expr { ) -> Expr {
self.require_normal_module_or_fn(hdl_attr); self.require_normal_module(hdl_attr);
let repeated_value = &expr_repeat.expr; let repeated_value = &expr_repeat.expr;
*expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=>
::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) ::fayalite::expr::ToExpr::to_expr(&(#repeated_value))
@ -35,10 +35,10 @@ impl Visitor<'_> {
} }
pub(crate) fn process_hdl_struct( pub(crate) fn process_hdl_struct(
&mut self, &mut self,
hdl_attr: HdlAttr<Nothing, kw::hdl>, hdl_attr: HdlAttr<Nothing>,
expr_struct: ExprStruct, expr_struct: ExprStruct,
) -> Expr { ) -> Expr {
self.require_normal_module_or_fn(&hdl_attr); self.require_normal_module(&hdl_attr);
let name_span = expr_struct.path.segments.last().unwrap().ident.span(); let name_span = expr_struct.path.segments.last().unwrap().ident.span();
let builder_ident = format_ident!("__builder", span = name_span); let builder_ident = format_ident!("__builder", span = name_span);
let empty_builder = if expr_struct.qself.is_some() let empty_builder = if expr_struct.qself.is_some()
@ -91,10 +91,10 @@ impl Visitor<'_> {
} }
pub(crate) fn process_hdl_tuple( pub(crate) fn process_hdl_tuple(
&mut self, &mut self,
hdl_attr: HdlAttr<Nothing, kw::hdl>, hdl_attr: HdlAttr<Nothing>,
expr_tuple: ExprTuple, expr_tuple: ExprTuple,
) -> Expr { ) -> Expr {
self.require_normal_module_or_fn(hdl_attr); self.require_normal_module(hdl_attr);
parse_quote_spanned! {expr_tuple.span()=> parse_quote_spanned! {expr_tuple.span()=>
::fayalite::expr::ToExpr::to_expr(&#expr_tuple) ::fayalite::expr::ToExpr::to_expr(&#expr_tuple)
} }

View file

@ -2,112 +2,22 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
fold::{impl_fold, DoFold}, fold::{impl_fold, DoFold},
kw, module::transform_body::{with_debug_clone_and_fold, Visitor},
module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor},
Errors, HdlAttr, PairsIterExt, Errors, HdlAttr, PairsIterExt,
}; };
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
use std::collections::BTreeSet;
use syn::{ use syn::{
fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold}, fold::{fold_arm, fold_expr_match, fold_pat, Fold},
parse::Nothing, parse::Nothing,
parse_quote_spanned, parse_quote_spanned,
punctuated::Punctuated, punctuated::Punctuated,
spanned::Spanned, spanned::Spanned,
token::{Brace, Paren}, token::{Brace, Paren},
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr, Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Member, Pat, PatIdent, PatOr, PatParen,
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment, PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath,
Token, TypePath,
}; };
macro_rules! visit_trait {
(
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)*
) => {
trait VisitMatchPat<'a> {
$(fn $fn(&mut self, $value: &'a $Value) {
$fn(self, $value);
})*
}
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)*
};
}
visit_trait! {
fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) {
let MatchPatBinding { ident: _ } = v;
}
fn visit_match_pat_wild(_state: _, v: &MatchPatWild) {
let MatchPatWild { underscore_token: _ } = v;
}
fn visit_match_pat_rest(_state: _, v: &MatchPatRest) {
let MatchPatRest { dot2_token: _ } = v;
}
fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) {
let MatchPatParen { paren_token: _, pat } = v;
state.visit_match_pat(pat);
}
fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) {
let MatchPatParen { paren_token: _, pat } = v;
state.visit_match_pat_simple(pat);
}
fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) {
let MatchPatOr { leading_vert: _, cases } = v;
for v in cases {
state.visit_match_pat(v);
}
}
fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) {
let MatchPatOr { leading_vert: _, cases } = v;
for v in cases {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) {
let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
state.visit_match_pat_simple(pat);
}
fn visit_match_pat_struct(state: _, v: &MatchPatStruct) {
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
for v in fields {
state.visit_match_pat_struct_field(v);
}
}
fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) {
let MatchPatTuple { paren_token: _, fields } = v;
for v in fields {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) {
let MatchPatEnumVariant {match_span:_, variant_path: _, enum_path: _, variant_name: _, field } = v;
if let Some((_, v)) = field {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_simple(state: _, v: &MatchPatSimple) {
match v {
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v),
MatchPatSimple::Binding(v) => state.visit_match_pat_binding(v),
MatchPatSimple::Wild(v) => state.visit_match_pat_wild(v),
MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v),
}
}
fn visit_match_pat(state: _, v: &MatchPat) {
match v {
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
MatchPat::Or(v) => state.visit_match_pat_or(v),
MatchPat::Paren(v) => state.visit_match_pat_paren(v),
MatchPat::Struct(v) => state.visit_match_pat_struct(v),
MatchPat::Tuple(v) => state.visit_match_pat_tuple(v),
MatchPat::EnumVariant(v) => state.visit_match_pat_enum_variant(v),
}
}
}
with_debug_clone_and_fold! { with_debug_clone_and_fold! {
struct MatchPatBinding<> { struct MatchPatBinding<> {
ident: Ident, ident: Ident,
@ -142,15 +52,6 @@ with_debug_clone_and_fold! {
} }
} }
impl<P> MatchPatOr<P> {
/// returns the first `|` between two patterns
fn first_inner_vert(&self) -> Option<Token![|]> {
let mut pairs = self.cases.pairs();
pairs.next_back();
pairs.next().and_then(|v| v.into_tuple().1.copied())
}
}
impl<P: ToTokens> ToTokens for MatchPatOr<P> { impl<P: ToTokens> ToTokens for MatchPatOr<P> {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let Self { let Self {
@ -175,19 +76,6 @@ impl ToTokens for MatchPatWild {
} }
} }
with_debug_clone_and_fold! {
struct MatchPatRest<> {
dot2_token: Token![..],
}
}
impl ToTokens for MatchPatRest {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self { dot2_token } = self;
dot2_token.to_tokens(tokens);
}
}
with_debug_clone_and_fold! { with_debug_clone_and_fold! {
struct MatchPatStructField<> { struct MatchPatStructField<> {
field_name: Ident, field_name: Ident,
@ -270,25 +158,6 @@ impl ToTokens for MatchPatStruct {
} }
} }
with_debug_clone_and_fold! {
struct MatchPatTuple<> {
paren_token: Paren,
fields: Punctuated<MatchPatSimple, Token![,]>,
}
}
impl ToTokens for MatchPatTuple {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
paren_token,
fields,
} = self;
paren_token.surround(tokens, |tokens| {
fields.to_tokens(tokens);
})
}
}
with_debug_clone_and_fold! { with_debug_clone_and_fold! {
struct MatchPatEnumVariant<> { struct MatchPatEnumVariant<> {
match_span: Span, match_span: Span,
@ -324,7 +193,6 @@ enum MatchPatSimple {
Or(MatchPatOr<MatchPatSimple>), Or(MatchPatOr<MatchPatSimple>),
Binding(MatchPatBinding), Binding(MatchPatBinding),
Wild(MatchPatWild), Wild(MatchPatWild),
Rest(MatchPatRest),
} }
impl_fold! { impl_fold! {
@ -333,7 +201,6 @@ impl_fold! {
Or(MatchPatOr<MatchPatSimple>), Or(MatchPatOr<MatchPatSimple>),
Binding(MatchPatBinding), Binding(MatchPatBinding),
Wild(MatchPatWild), Wild(MatchPatWild),
Rest(MatchPatRest),
} }
} }
@ -344,7 +211,6 @@ impl ToTokens for MatchPatSimple {
Self::Paren(v) => v.to_tokens(tokens), Self::Paren(v) => v.to_tokens(tokens),
Self::Binding(v) => v.to_tokens(tokens), Self::Binding(v) => v.to_tokens(tokens),
Self::Wild(v) => v.to_tokens(tokens), Self::Wild(v) => v.to_tokens(tokens),
Self::Rest(v) => v.to_tokens(tokens),
} }
} }
} }
@ -411,7 +277,6 @@ trait ParseMatchPat: Sized {
fn or(v: MatchPatOr<Self>) -> Self; fn or(v: MatchPatOr<Self>) -> Self;
fn paren(v: MatchPatParen<Self>) -> Self; fn paren(v: MatchPatParen<Self>) -> Self;
fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()>; fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()>;
fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()>;
fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant) fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant)
-> Result<Self, ()>; -> Result<Self, ()>;
fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> { fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> {
@ -596,34 +461,7 @@ trait ParseMatchPat: Sized {
}) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild { }) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild {
underscore_token, underscore_token,
}))), }))),
Pat::Tuple(PatTuple { Pat::Tuple(_) | Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => {
attrs: _,
paren_token,
elems,
}) => {
let fields = elems
.into_pairs()
.filter_map_pair_value(|field_pat| {
if let Pat::Rest(PatRest {
attrs: _,
dot2_token,
}) = field_pat
{
Some(MatchPatSimple::Rest(MatchPatRest { dot2_token }))
} else {
MatchPatSimple::parse(state, field_pat).ok()
}
})
.collect();
Self::tuple(
state,
MatchPatTuple {
paren_token,
fields,
},
)
}
Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => {
state state
.errors .errors
.error(pat, "not yet implemented in #[hdl] patterns"); .error(pat, "not yet implemented in #[hdl] patterns");
@ -658,14 +496,6 @@ impl ParseMatchPat for MatchPatSimple {
Err(()) Err(())
} }
fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()> {
state.errors.push(syn::Error::new(
v.paren_token.span.open(),
"matching tuples is not yet implemented inside structs/enums in #[hdl] patterns",
));
Err(())
}
fn enum_variant( fn enum_variant(
state: &mut HdlMatchParseState<'_>, state: &mut HdlMatchParseState<'_>,
v: MatchPatEnumVariant, v: MatchPatEnumVariant,
@ -684,7 +514,6 @@ enum MatchPat {
Or(MatchPatOr<MatchPat>), Or(MatchPatOr<MatchPat>),
Paren(MatchPatParen<MatchPat>), Paren(MatchPatParen<MatchPat>),
Struct(MatchPatStruct), Struct(MatchPatStruct),
Tuple(MatchPatTuple),
EnumVariant(MatchPatEnumVariant), EnumVariant(MatchPatEnumVariant),
} }
@ -694,7 +523,6 @@ impl_fold! {
Or(MatchPatOr<MatchPat>), Or(MatchPatOr<MatchPat>),
Paren(MatchPatParen<MatchPat>), Paren(MatchPatParen<MatchPat>),
Struct(MatchPatStruct), Struct(MatchPatStruct),
Tuple(MatchPatTuple),
EnumVariant(MatchPatEnumVariant), EnumVariant(MatchPatEnumVariant),
} }
} }
@ -716,10 +544,6 @@ impl ParseMatchPat for MatchPat {
Ok(Self::Struct(v)) Ok(Self::Struct(v))
} }
fn tuple(_state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()> {
Ok(Self::Tuple(v))
}
fn enum_variant( fn enum_variant(
_state: &mut HdlMatchParseState<'_>, _state: &mut HdlMatchParseState<'_>,
v: MatchPatEnumVariant, v: MatchPatEnumVariant,
@ -735,7 +559,6 @@ impl ToTokens for MatchPat {
Self::Or(v) => v.to_tokens(tokens), Self::Or(v) => v.to_tokens(tokens),
Self::Paren(v) => v.to_tokens(tokens), Self::Paren(v) => v.to_tokens(tokens),
Self::Struct(v) => v.to_tokens(tokens), Self::Struct(v) => v.to_tokens(tokens),
Self::Tuple(v) => v.to_tokens(tokens),
Self::EnumVariant(v) => v.to_tokens(tokens), Self::EnumVariant(v) => v.to_tokens(tokens),
} }
} }
@ -798,6 +621,10 @@ struct RewriteAsCheckMatch {
} }
impl Fold for RewriteAsCheckMatch { impl Fold for RewriteAsCheckMatch {
fn fold_field_pat(&mut self, mut i: FieldPat) -> FieldPat {
i.colon_token = Some(Token![:](i.member.span()));
i
}
fn fold_pat(&mut self, pat: Pat) -> Pat { fn fold_pat(&mut self, pat: Pat) -> Pat {
match pat { match pat {
Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) { Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) {
@ -912,30 +739,6 @@ impl Fold for RewriteAsCheckMatch {
// don't recurse into expressions // don't recurse into expressions
i i
} }
fn fold_local(&mut self, mut let_stmt: Local) -> Local {
if let Some(syn::LocalInit {
eq_token,
expr: _,
diverge,
}) = let_stmt.init.take()
{
let_stmt.init = Some(syn::LocalInit {
eq_token,
expr: parse_quote_spanned! {self.span=>
__match_value
},
diverge: diverge.map(|(else_, _expr)| {
(
else_,
parse_quote_spanned! {self.span=>
match __infallible {}
},
)
}),
});
}
fold_local(self, let_stmt)
}
} }
struct HdlMatchParseState<'a> { struct HdlMatchParseState<'a> {
@ -943,126 +746,10 @@ struct HdlMatchParseState<'a> {
errors: &'a mut Errors, errors: &'a mut Errors,
} }
struct HdlLetPatVisitState<'a> {
errors: &'a mut Errors,
bindings: BTreeSet<&'a Ident>,
}
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) {
self.bindings.insert(&v.ident);
}
fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) {
if let Some(first_inner_vert) = v.first_inner_vert() {
self.errors.error(
first_inner_vert,
"or-patterns are not supported in let statements",
);
}
visit_match_pat_or(self, v);
}
fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) {
if let Some(first_inner_vert) = v.first_inner_vert() {
self.errors.error(
first_inner_vert,
"or-patterns are not supported in let statements",
);
}
visit_match_pat_or_simple(self, v);
}
fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) {
self.errors.error(v, "refutable pattern in let statement");
}
}
impl Visitor<'_> { impl Visitor<'_> {
pub(crate) fn process_hdl_let_pat(
&mut self,
_hdl_attr: HdlAttr<Nothing, kw::hdl>,
mut let_stmt: Local,
) -> Local {
let span = let_stmt.let_token.span();
if let Pat::Type(pat) = &mut let_stmt.pat {
*pat.ty = wrap_ty_with_expr((*pat.ty).clone());
}
let check_let_stmt = RewriteAsCheckMatch { span }.fold_local(let_stmt.clone());
let Local {
attrs: _,
let_token,
pat,
init,
semi_token,
} = let_stmt;
self.require_normal_module_or_fn(let_token);
let Some(syn::LocalInit {
eq_token,
expr,
diverge,
}) = init
else {
self.errors
.error(let_token, "#[hdl] let must be assigned a value");
return empty_let();
};
if let Some((else_, _)) = diverge {
// TODO: implement let-else
self.errors
.error(else_, "#[hdl] let ... else { ... } is not implemented");
return empty_let();
}
let Ok(pat) = MatchPat::parse(
&mut HdlMatchParseState {
match_span: span,
errors: &mut self.errors,
},
pat,
) else {
return empty_let();
};
let mut state = HdlLetPatVisitState {
errors: &mut self.errors,
bindings: BTreeSet::new(),
};
state.visit_match_pat(&pat);
let HdlLetPatVisitState {
errors: _,
bindings,
} = state;
let retval = parse_quote_spanned! {span=>
let (#(#bindings,)* __scope,) = {
type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant;
let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| {
#[allow(unused_variables)]
#check_let_stmt
match __infallible {}
});
let mut __match_iter = ::fayalite::module::match_(__match_expr);
let ::fayalite::__std::option::Option::Some(__match_variant) = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else {
::fayalite::__std::unreachable!("#[hdl] let with uninhabited type");
};
let ::fayalite::__std::option::Option::None = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else {
::fayalite::__std::unreachable!("#[hdl] let with refutable pattern");
};
let (__match_variant, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
__match_variant,
);
#let_token #pat #eq_token __match_variant #semi_token
(#(#bindings,)* __scope,)
};
};
match retval {
syn::Stmt::Local(retval) => retval,
_ => unreachable!(),
}
}
pub(crate) fn process_hdl_match( pub(crate) fn process_hdl_match(
&mut self, &mut self,
_hdl_attr: HdlAttr<Nothing, kw::hdl>, _hdl_attr: HdlAttr<Nothing>,
expr_match: ExprMatch, expr_match: ExprMatch,
) -> Expr { ) -> Expr {
let span = expr_match.match_token.span(); let span = expr_match.match_token.span();
@ -1074,7 +761,7 @@ impl Visitor<'_> {
brace_token: _, brace_token: _,
arms, arms,
} = expr_match; } = expr_match;
self.require_normal_module_or_fn(match_token); self.require_normal_module(match_token);
let mut state = HdlMatchParseState { let mut state = HdlMatchParseState {
match_span: span, match_span: span,
errors: &mut self.errors, errors: &mut self.errors,

File diff suppressed because it is too large Load diff

View file

@ -16,4 +16,4 @@ version.workspace = true
proc-macro = true proc-macro = true
[dependencies] [dependencies]
fayalite-proc-macros-impl.workspace = true fayalite-proc-macros-impl = { workspace = true }

View file

@ -13,11 +13,11 @@ rust-version.workspace = true
version.workspace = true version.workspace = true
[dependencies] [dependencies]
indexmap.workspace = true indexmap = { workspace = true }
prettyplease.workspace = true prettyplease = { workspace = true }
proc-macro2.workspace = true proc-macro2 = { workspace = true }
quote.workspace = true quote = { workspace = true }
serde.workspace = true serde = { workspace = true }
serde_json.workspace = true serde_json = { workspace = true }
syn.workspace = true syn = { workspace = true }
thiserror.workspace = true thiserror = { workspace = true }

View file

@ -14,29 +14,22 @@ rust-version.workspace = true
version.workspace = true version.workspace = true
[dependencies] [dependencies]
bitvec.workspace = true bitvec = { workspace = true }
blake3.workspace = true hashbrown = { workspace = true }
clap.workspace = true num-bigint = { workspace = true }
ctor.workspace = true num-traits = { workspace = true }
eyre.workspace = true fayalite-proc-macros = { workspace = true }
fayalite-proc-macros.workspace = true serde = { workspace = true }
hashbrown.workspace = true serde_json = { workspace = true }
jobslot.workspace = true clap = { version = "4.5.9", features = ["derive", "env"] }
num-bigint.workspace = true eyre = "0.6.12"
num-traits.workspace = true which = "6.0.1"
os_pipe.workspace = true
petgraph.workspace = true
serde_json.workspace = true
serde.workspace = true
tempfile.workspace = true
vec_map.workspace = true
which.workspace = true
[dev-dependencies] [dev-dependencies]
trybuild.workspace = true trybuild = { workspace = true }
[build-dependencies] [build-dependencies]
fayalite-visit-gen.workspace = true fayalite-visit-gen = { workspace = true }
[features] [features]
unstable-doc = [] unstable-doc = []

View file

@ -5,9 +5,6 @@ use std::{env, fs, path::Path};
fn main() { fn main() {
println!("cargo::rustc-check-cfg=cfg(todo)"); println!("cargo::rustc-check-cfg=cfg(todo)");
println!("cargo::rustc-check-cfg=cfg(cfg_false_for_tests)");
println!("cargo::rustc-check-cfg=cfg(cfg_true_for_tests)");
println!("cargo::rustc-cfg=cfg_true_for_tests");
let path = "visit_types.json"; let path = "visit_types.json";
println!("cargo::rerun-if-changed={path}"); println!("cargo::rerun-if-changed={path}");
println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=build.rs");

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use clap::Parser; use clap::Parser;
use fayalite::{cli, prelude::*}; use fayalite::{cli, prelude::*};
@ -17,15 +15,15 @@ fn blinky(clock_frequency: u64) {
let max_value = clock_frequency / 2 - 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: 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]
if counter_reg.cmp_eq(max_value) { if counter.cmp_eq(max_value) {
connect_any(counter_reg, 0u8); connect_any(counter, 0u8);
connect(output_reg, !output_reg); connect(output_reg, !output_reg);
} else { } else {
connect_any(counter_reg, counter_reg + 1_hdl_u1); connect_any(counter, counter + 1_hdl_u1);
} }
#[hdl] #[hdl]
let led: Bool = m.output(); let led: Bool = m.output();

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
//! //!

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # Fayalite Modules //! # Fayalite Modules
//! //!
//! The [`#[hdl_module]`][`crate::hdl_module`] attribute is applied to a Rust //! The [`#[hdl_module]`][`crate::hdl_module`] attribute is applied to a Rust

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! These are for when you want to use modules written in //! These are for when you want to use modules written in
//! some other language, such as Verilog. //! some other language, such as Verilog.
//! //!

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # Module Function Bodies //! # Module Function Bodies
//! //!
//! The `#[hdl_module]` attribute lets you have statements/expressions with `#[hdl]` annotations //! The `#[hdl_module]` attribute lets you have statements/expressions with `#[hdl]` annotations

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # `#[hdl]` Array Expressions //! # `#[hdl]` Array Expressions
//! //!
//! `#[hdl]` can be used on Array Expressions to construct an [`Array<[T; N]>`][type@Array] expression: //! `#[hdl]` can be used on Array Expressions to construct an [`Array<[T; N]>`][type@Array] expression:

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # `#[hdl] if` Statements //! # `#[hdl] if` Statements
//! //!
//! `#[hdl] if` statements behave similarly to Rust `if` statements, except they end up as muxes //! `#[hdl] if` statements behave similarly to Rust `if` statements, except they end up as muxes

View file

@ -1,8 +1,5 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! ## `#[hdl] let` statements //! ## `#[hdl] let` statements
pub mod destructuring;
pub mod inputs_outputs; pub mod inputs_outputs;
pub mod instances; pub mod instances;
pub mod memories; pub mod memories;

View file

@ -1,33 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! ### Destructuring Let
//!
//! You can use `#[hdl] let` to destructure types, similarly to Rust `let` statements with non-trivial patterns.
//!
//! `#[hdl] let` statements can only match one level of struct/tuple pattern for now,
//! e.g. you can match with the pattern `MyStruct { a, b }`, but not `MyStruct { a, b: Struct2 { v } }`.
//!
//! ```
//! # use fayalite::prelude::*;
//! #[hdl]
//! struct MyStruct {
//! a: UInt<8>,
//! b: Bool,
//! }
//!
//! #[hdl_module]
//! fn my_module() {
//! #[hdl]
//! let my_input: MyStruct = m.input();
//! #[hdl]
//! let my_output: UInt<8> = m.input();
//! #[hdl]
//! let MyStruct { a, b } = my_input;
//! #[hdl]
//! if b {
//! connect(my_output, a);
//! } else {
//! connect(my_output, 0_hdl_u8);
//! }
//! }
//! ```

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! ### Inputs/Outputs //! ### Inputs/Outputs
//! //!
//! Inputs/Outputs create a Rust variable with type [`Expr<T>`] where `T` is the type of the input/output. //! Inputs/Outputs create a Rust variable with type [`Expr<T>`] where `T` is the type of the input/output.

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! ### Module Instances //! ### Module Instances
//! //!
//! module instances are kinda like the hardware equivalent of calling a function, //! module instances are kinda like the hardware equivalent of calling a function,

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # Memories //! # Memories
//! //!
//! Memories are optimized for storing large amounts of data. //! Memories are optimized for storing large amounts of data.

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! ### Registers //! ### Registers
//! //!
//! Registers are memory devices that will change their state only on a clock //! Registers are memory devices that will change their state only on a clock
@ -9,9 +7,6 @@
//! //!
//! Registers follow [connection semantics], which are unlike assignments in software, so you should read it. //! Registers follow [connection semantics], which are unlike assignments in software, so you should read it.
//! //!
//! By convention, register names end in `_reg` -- this helps you tell which values are written
//! immediately or on the next clock edge when connecting to them.
//!
//! ``` //! ```
//! # use fayalite::prelude::*; //! # use fayalite::prelude::*;
//! # #[hdl_module] //! # #[hdl_module]
@ -21,11 +16,11 @@
//! #[hdl] //! #[hdl]
//! let cd: ClockDomain = m.input(); //! let cd: ClockDomain = m.input();
//! #[hdl] //! #[hdl]
//! let my_reg: UInt<8> = reg_builder().clock_domain(cd).reset(8_hdl_u8); //! let my_register: UInt<8> = reg_builder().clock_domain(cd).reset(8_hdl_u8);
//! #[hdl] //! #[hdl]
//! if v { //! if v {
//! // my_reg is only changed when both `v` is set and `cd`'s clock edge occurs. //! // my_register is only changed when both `v` is set and `cd`'s clock edge occurs.
//! connect(my_reg, 0x45_hdl_u8); //! connect(my_register, 0x45_hdl_u8);
//! } //! }
//! # } //! # }
//! ``` //! ```

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! ### Wires //! ### Wires
//! //!
//! Wires are kinda like variables, but unlike registers, //! Wires are kinda like variables, but unlike registers,

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # `_hdl`-suffixed literals //! # `_hdl`-suffixed literals
//! //!
//! You can have integer literals with an arbitrary number of bits like so: //! You can have integer literals with an arbitrary number of bits like so:

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # `#[hdl] match` Statements //! # `#[hdl] match` Statements
//! //!
//! `#[hdl] match` statements behave similarly to Rust `match` statements, except they end up as muxes //! `#[hdl] match` statements behave similarly to Rust `match` statements, except they end up as muxes
@ -7,5 +5,5 @@
//! //!
//! `#[hdl] match` statements' bodies must evaluate to type `()` for now. //! `#[hdl] match` statements' bodies must evaluate to type `()` for now.
//! //!
//! `#[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/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(_))`.

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # `#[hdl]` Struct/Variant Expressions //! # `#[hdl]` Struct/Variant Expressions
//! //!
//! Note: Structs are also known as [Bundles] when used in Fayalite, the Bundle name comes from [FIRRTL]. //! Note: Structs are also known as [Bundles] when used in Fayalite, the Bundle name comes from [FIRRTL].

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # Normal Modules //! # Normal Modules
//! //!
//! See also: [Extern Modules][`super::extern_module`] //! See also: [Extern Modules][`super::extern_module`]

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # Fayalite Semantics //! # Fayalite Semantics
//! //!
//! Fayalite's semantics are based on [FIRRTL]. Due to their significance, some of the semantics are also documented here. //! Fayalite's semantics are based on [FIRRTL]. Due to their significance, some of the semantics are also documented here.

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! # Connection Semantics //! # Connection Semantics
//! //!
//! Fayalite's connection semantics are unlike assignments in software, so be careful! //! Fayalite's connection semantics are unlike assignments in software, so be careful!

View file

@ -8,7 +8,6 @@ use serde::{Deserialize, Serialize};
use std::{ use std::{
fmt, fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
iter::FusedIterator,
ops::Deref, ops::Deref,
}; };
@ -119,87 +118,11 @@ pub struct CustomFirrtlAnnotation {
pub additional_fields: CustomFirrtlAnnotationFields, pub additional_fields: CustomFirrtlAnnotationFields,
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct DontTouchAnnotation; #[non_exhaustive]
pub enum Annotation {
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] DontTouch,
pub struct SVAttributeAnnotation { CustomFirrtl(CustomFirrtlAnnotation),
pub text: Interned<str>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub struct BlackBoxInlineAnnotation {
pub path: Interned<str>,
pub text: Interned<str>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub struct BlackBoxPathAnnotation {
pub path: Interned<str>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub struct DocStringAnnotation {
pub text: Interned<str>,
}
macro_rules! make_annotation_enum {
(
$(#[$meta:meta])*
$vis:vis enum $Annotation:ident {
$($Variant:ident($T:ident),)*
}
) => {
$(#[$meta])*
$vis enum $Annotation {
$($Variant($T),)*
}
$(impl IntoAnnotations for $T {
type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[$Annotation::$Variant(self)]
}
}
impl IntoAnnotations for &'_ $T {
type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[$Annotation::$Variant(*self)]
}
}
impl IntoAnnotations for &'_ mut $T {
type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[$Annotation::$Variant(*self)]
}
}
impl IntoAnnotations for Box<$T> {
type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[$Annotation::$Variant(*self)]
}
})*
};
}
make_annotation_enum! {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum Annotation {
DontTouch(DontTouchAnnotation),
SVAttribute(SVAttributeAnnotation),
BlackBoxInline(BlackBoxInlineAnnotation),
BlackBoxPath(BlackBoxPathAnnotation),
DocString(DocStringAnnotation),
CustomFirrtl(CustomFirrtlAnnotation),
}
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
@ -264,70 +187,10 @@ impl IntoAnnotations for &'_ mut Annotation {
} }
} }
pub struct IterIntoAnnotations<T: Iterator<Item: IntoAnnotations>> { impl<T: IntoIterator<Item = Annotation>> IntoAnnotations for T {
outer: T, type IntoAnnotations = Self;
inner: Option<<<T::Item as IntoAnnotations>::IntoAnnotations as IntoIterator>::IntoIter>,
}
impl<T: Iterator<Item: IntoAnnotations>> Iterator for IterIntoAnnotations<T> {
type Item = Annotation;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(inner) = &mut self.inner {
let Some(retval) = inner.next() else {
self.inner = None;
continue;
};
return Some(retval);
} else {
self.inner = Some(self.outer.next()?.into_annotations().into_iter());
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
if let (0, Some(0)) = self.outer.size_hint() {
self.inner
.as_ref()
.map(|v| v.size_hint())
.unwrap_or((0, Some(0)))
} else {
(
self.inner.as_ref().map(|v| v.size_hint().0).unwrap_or(0),
None,
)
}
}
fn fold<B, F>(self, init: B, f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
self.inner
.into_iter()
.chain(self.outer.map(|v| v.into_annotations().into_iter()))
.flatten()
.fold(init, f)
}
}
impl<
T: FusedIterator<
Item: IntoAnnotations<IntoAnnotations: IntoIterator<IntoIter: FusedIterator>>,
>,
> FusedIterator for IterIntoAnnotations<T>
{
}
impl<T: IntoIterator<Item: IntoAnnotations>> IntoAnnotations for T {
type IntoAnnotations = IterIntoAnnotations<T::IntoIter>;
fn into_annotations(self) -> Self::IntoAnnotations { fn into_annotations(self) -> Self::IntoAnnotations {
IterIntoAnnotations { self
outer: self.into_iter(),
inner: None,
}
} }
} }

View file

@ -2,11 +2,8 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{ expr::{ops::ArrayIndex, Expr, ToExpr},
ops::{ArrayIndex, ArrayLiteral, ExprPartialEq}, int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
},
int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE},
intern::{Intern, Interned, LazyInterned}, intern::{Intern, Interned, LazyInterned},
module::transform::visit::{Fold, Folder, Visit, Visitor}, module::transform::visit::{Fold, Folder, Visit, Visitor},
source_location::SourceLocation, source_location::SourceLocation,
@ -88,7 +85,7 @@ impl<T: Type, Len: Size> ArrayType<T, Len> {
} }
} }
impl<T: Type, Len: KnownSize + Size<SizeType = Len>> ArrayType<T, Len> { impl<T: Type, Len: KnownSize> ArrayType<T, Len> {
pub fn new_static(element: T) -> Self { pub fn new_static(element: T) -> Self {
Self::new(element, Len::SizeType::default()) Self::new(element, Len::SizeType::default())
} }
@ -190,7 +187,7 @@ impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
let base = Expr::as_dyn_array(*this); let base = Expr::as_dyn_array(*this);
let base_ty = Expr::ty(base); let base_ty = Expr::ty(base);
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
Interned::into_inner(Intern::intern_sized( Interned::<_>::into_inner(Intern::intern_sized(
Len::ArrayMatch::<T>::try_from(retval) Len::ArrayMatch::<T>::try_from(retval)
.ok() .ok()
.expect("unreachable"), .expect("unreachable"),
@ -205,7 +202,7 @@ impl<T: Type> Index<T> for ArrayWithoutGenerics {
type Output = ArrayWithoutLen<T>; type Output = ArrayWithoutLen<T>;
fn index(&self, element: T) -> &Self::Output { fn index(&self, element: T) -> &Self::Output {
Interned::into_inner(Intern::intern_sized(ArrayWithoutLen { element })) Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
} }
} }
@ -218,39 +215,6 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
type Output = ArrayType<T, L::Size>; type Output = ArrayType<T, L::Size>;
fn index(&self, len: L) -> &Self::Output { fn index(&self, len: L) -> &Self::Output {
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) Interned::<_>::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
}
}
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
where
Lhs: ExprPartialEq<Rhs>,
{
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
let lhs_ty = Expr::ty(lhs);
let rhs_ty = Expr::ty(rhs);
assert_eq!(lhs_ty.len(), rhs_ty.len());
ArrayLiteral::<Bool, DynSize>::new(
Bool,
(0..lhs_ty.len())
.map(|i| Expr::canonical(lhs[i].cmp_eq(rhs[i])))
.collect(),
)
.cast_to_bits()
.all_one_bits()
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
let lhs_ty = Expr::ty(lhs);
let rhs_ty = Expr::ty(rhs);
assert_eq!(lhs_ty.len(), rhs_ty.len());
ArrayLiteral::<Bool, DynSize>::new(
Bool,
(0..lhs_ty.len())
.map(|i| Expr::canonical(lhs[i].cmp_ne(rhs[i])))
.collect(),
)
.cast_to_bits()
.any_one_bits()
} }
} }

View file

@ -2,20 +2,14 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{ expr::{ops::BundleLiteral, Expr, ToExpr},
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
CastToBits, Expr, ReduceBits, ToExpr,
},
int::{Bool, DynSize},
intern::{Intern, Interned}, intern::{Intern, Interned},
sim::{SimValue, ToSimValue},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type, impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type,
TypeProperties, TypeWithDeref, TypeProperties, TypeWithDeref,
}, },
}; };
use bitvec::vec::BitVec;
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{fmt, marker::PhantomData}; use std::{fmt, marker::PhantomData};
@ -150,12 +144,6 @@ impl BundleTypePropertiesBuilder {
} }
} }
impl Default for BundleTypePropertiesBuilder {
fn default() -> Self {
Self::new()
}
}
impl Bundle { impl Bundle {
#[track_caller] #[track_caller]
pub fn new(fields: Interned<[BundleField]>) -> Self { pub fn new(fields: Interned<[BundleField]>) -> Self {
@ -329,19 +317,7 @@ macro_rules! impl_tuple_builder_fields {
} }
macro_rules! impl_tuples { macro_rules! impl_tuples {
( ([$({#[num = $num:literal, field = $field:ident] $var:ident: $T:ident})*] []) => {
[$({
#[
num = $num:tt,
field = $field:ident,
ty = $ty_var:ident: $Ty:ident,
lhs = $lhs_var:ident: $Lhs:ident,
rhs = $rhs_var:ident: $Rhs:ident
]
$var:ident: $T:ident
})*]
[]
) => {
impl_tuple_builder_fields! { impl_tuple_builder_fields! {
{} {}
[$({ [$({
@ -366,7 +342,6 @@ macro_rules! impl_tuples {
std::iter::once(MatchVariantWithoutScope(($(Expr::field(this, stringify!($num)),)*))) std::iter::once(MatchVariantWithoutScope(($(Expr::field(this, stringify!($num)),)*)))
} }
fn mask_type(&self) -> Self::MaskType { fn mask_type(&self) -> Self::MaskType {
#![allow(clippy::unused_unit)]
let ($($var,)*) = self; let ($($var,)*) = self;
($($var.mask_type(),)*) ($($var.mask_type(),)*)
} }
@ -375,7 +350,6 @@ macro_rules! impl_tuples {
} }
#[track_caller] #[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self { fn from_canonical(canonical_type: CanonicalType) -> Self {
#![allow(clippy::unused_unit)]
let CanonicalType::Bundle(bundle) = canonical_type else { let CanonicalType::Bundle(bundle) = canonical_type else {
panic!("expected bundle"); panic!("expected bundle");
}; };
@ -384,7 +358,7 @@ macro_rules! impl_tuples {
}; };
$(let BundleField { name, flipped, ty } = $var; $(let BundleField { name, flipped, ty } = $var;
assert_eq!(&*name, stringify!($num)); assert_eq!(&*name, stringify!($num));
assert!(!flipped); assert_eq!(flipped, false);
let $var = $T::from_canonical(ty);)* let $var = $T::from_canonical(ty);)*
($($var,)*) ($($var,)*)
} }
@ -403,7 +377,7 @@ macro_rules! impl_tuples {
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) { impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant { fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
let _ = this; let _ = this;
Interned::into_inner(($(Expr::field(*this, stringify!($num)),)*).intern_sized()) Interned::<_>::into_inner(($(Expr::field(*this, stringify!($num)),)*).intern_sized())
} }
} }
impl<$($T: StaticType,)*> StaticType for ($($T,)*) { impl<$($T: StaticType,)*> StaticType for ($($T,)*) {
@ -441,102 +415,6 @@ macro_rules! impl_tuples {
BundleLiteral::new(ty, field_values[..].intern()).to_expr() BundleLiteral::new(ty, field_values[..].intern()).to_expr()
} }
} }
impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<CanonicalType> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<Bundle>::to_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType>
{
ToSimValue::<Bundle>::into_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<Bundle>::box_into_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
}
impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<Bundle> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> {
let ($($var,)*) = self;
let [$($ty_var,)*] = *ty.fields() else {
panic!("bundle has wrong number of fields");
};
$(let $var = $var.to_sim_value($ty_var.ty);)*
ToSimValue::into_sim_value(($($var,)*), ty)
}
#[track_caller]
fn into_sim_value(self, ty: Bundle) -> SimValue<Bundle> {
#![allow(unused_mut)]
#![allow(clippy::unused_unit)]
let ($($var,)*) = self;
let [$($ty_var,)*] = *ty.fields() else {
panic!("bundle has wrong number of fields");
};
let mut bits: Option<BitVec> = None;
$(let $var = $var.into_sim_value($ty_var.ty);
assert_eq!($var.ty(), $ty_var.ty);
if !$var.bits().is_empty() {
if let Some(bits) = &mut bits {
bits.extend_from_bitslice($var.bits());
} else {
let mut $var = $var.into_bits();
$var.reserve(ty.type_properties().bit_width - $var.len());
bits = Some($var);
}
}
)*
bits.unwrap_or_else(BitVec::new).into_sim_value(ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: Bundle) -> SimValue<Bundle> {
Self::into_sim_value(*self, ty)
}
}
impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
let ($($var,)*) = self;
let ($($ty_var,)*) = ty;
$(let $var = $var.to_sim_value($ty_var).into_canonical();)*
SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical()))
}
#[track_caller]
fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
let ($($var,)*) = self;
let ($($ty_var,)*) = ty;
$(let $var = $var.into_sim_value($ty_var).into_canonical();)*
SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical()))
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
Self::into_sim_value(*self, ty)
}
}
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
let ($($lhs_var,)*) = *lhs;
let ($($rhs_var,)*) = *rhs;
ArrayLiteral::<Bool, DynSize>::new(
Bool,
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
)
.cast_to_bits()
.all_one_bits()
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
let ($($lhs_var,)*) = *lhs;
let ($($rhs_var,)*) = *rhs;
ArrayLiteral::<Bool, DynSize>::new(
Bool,
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
)
.cast_to_bits()
.any_one_bits()
}
}
}; };
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
impl_tuples!([$($lhs)*] []); impl_tuples!([$($lhs)*] []);
@ -546,123 +424,17 @@ macro_rules! impl_tuples {
impl_tuples! { impl_tuples! {
[] [ [] [
{#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0} {#[num = 0, field = field_0] v0: T0}
{#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1} {#[num = 1, field = field_1] v1: T1}
{#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2} {#[num = 2, field = field_2] v2: T2}
{#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3} {#[num = 3, field = field_3] v3: T3}
{#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4} {#[num = 4, field = field_4] v4: T4}
{#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5} {#[num = 5, field = field_5] v5: T5}
{#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6} {#[num = 6, field = field_6] v6: T6}
{#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7} {#[num = 7, field = field_7] v7: T7}
{#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8} {#[num = 8, field = field_8] v8: T8}
{#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9} {#[num = 9, field = field_9] v9: T9}
{#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10} {#[num = 10, field = field_10] v10: T10}
{#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11} {#[num = 11, field = field_11] v11: T11}
] ]
} }
impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
type BaseType = Bundle;
type MaskType = ();
type MatchVariant = PhantomData<T>;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants(
this: Expr<Self>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter {
let _ = this;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(PhantomData))
}
fn mask_type(&self) -> Self::MaskType {
()
}
fn canonical(&self) -> CanonicalType {
Bundle::new(self.fields()).canonical()
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::Bundle(bundle) = canonical_type else {
panic!("expected bundle");
};
assert!(
bundle.fields().is_empty(),
"bundle has wrong number of fields"
);
PhantomData
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
}
pub struct PhantomDataBuilder<T: ?Sized + Send + Sync + 'static>(PhantomData<T>);
impl<T: ?Sized + Send + Sync + 'static> Default for PhantomDataBuilder<T> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
type Type = PhantomData<T>;
fn to_expr(&self) -> Expr<Self::Type> {
PhantomData.to_expr()
}
}
impl<T: ?Sized + Send + Sync + 'static> BundleType for PhantomData<T> {
type Builder = PhantomDataBuilder<T>;
type FilledBuilder = PhantomDataBuilder<T>;
fn fields(&self) -> Interned<[BundleField]> {
Interned::default()
}
}
impl<T: ?Sized + Send + Sync + 'static> TypeWithDeref for PhantomData<T> {
fn expr_deref(_this: &Expr<Self>) -> &Self::MatchVariant {
&PhantomData
}
}
impl<T: ?Sized + Send + Sync + 'static> StaticType for PhantomData<T> {
const TYPE: Self = PhantomData;
const MASK_TYPE: Self::MaskType = ();
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
type Type = PhantomData<T>;
fn to_expr(&self) -> Expr<Self::Type> {
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
}
}
impl<T: ?Sized + Send + Sync + 'static> ToSimValue<Self> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: Self) -> SimValue<Self> {
ToSimValue::into_sim_value(BitVec::new(), ty)
}
}
impl<T: ?Sized> ToSimValue<Bundle> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> {
assert!(ty.fields().is_empty());
ToSimValue::into_sim_value(BitVec::new(), ty)
}
}
impl<T: ?Sized> ToSimValue<CanonicalType> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
let ty = Bundle::from_canonical(ty);
assert!(ty.fields().is_empty());
ToSimValue::into_sim_value(BitVec::new(), ty).into_canonical()
}
}

View file

@ -1,27 +1,15 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{ use crate::{
bundle::{Bundle, BundleType}, bundle::{Bundle, BundleType},
firrtl::{self, ExportOptions}, firrtl,
intern::Interned, intern::Interned,
module::Module, module::Module,
util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8},
}; };
use clap::{ use clap::{
builder::{OsStringValueParser, TypedValueParser}, builder::{OsStringValueParser, TypedValueParser},
Parser, Subcommand, ValueEnum, ValueHint, Args, Parser, Subcommand, ValueEnum, ValueHint,
}; };
use eyre::{eyre, Report}; use eyre::{eyre, Report};
use serde::{Deserialize, Serialize}; use std::{error, ffi::OsString, fmt, io, path::PathBuf, process};
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 type Result<T = (), E = CliError> = std::result::Result<T, E>;
@ -49,157 +37,74 @@ impl From<io::Error> for CliError {
pub trait RunPhase<Arg> { pub trait RunPhase<Arg> {
type Output; type Output;
fn run(&self, arg: Arg) -> Result<Self::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)] #[derive(Args, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct BaseArgs { pub struct BaseArgs {
/// the directory to put the generated main output file and associated files in /// the directory to put the generated main output file and associated files in
#[arg(short, long, value_hint = ValueHint::DirPath, required = true)] #[arg(short, long, value_hint = ValueHint::DirPath)]
pub output: Option<PathBuf>, pub output: PathBuf,
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
#[arg(long)] #[arg(long)]
pub file_stem: Option<String>, 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 { impl BaseArgs {
fn make_firrtl_file_backend(&self) -> Result<(firrtl::FileBackend, Option<TempDir>)> { pub fn to_firrtl_file_backend(&self) -> firrtl::FileBackend {
let (dir_path, temp_dir) = match &self.output { firrtl::FileBackend {
Some(output) => (output.clone(), None), dir_path: self.output.clone(),
None => { top_fir_file_stem: self.file_stem.clone(),
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)] #[derive(Args, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct FirrtlArgs { pub struct FirrtlArgs {
#[command(flatten)] #[command(flatten)]
pub base: BaseArgs, pub base: BaseArgs,
#[command(flatten)]
pub export_options: ExportOptions,
} }
#[derive(Debug)] #[derive(Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct FirrtlOutput { pub struct FirrtlOutput {
pub file_stem: String, pub file_stem: String,
pub top_module: String,
pub output_dir: PathBuf,
pub temp_dir: Option<TempDir>,
} }
impl FirrtlOutput { impl FirrtlOutput {
pub fn file_with_ext(&self, ext: &str) -> PathBuf { pub fn firrtl_file(&self, args: &FirrtlArgs) -> PathBuf {
let mut retval = self.output_dir.join(&self.file_stem); let mut retval = args.base.output.join(&self.file_stem);
retval.set_extension(ext); retval.set_extension("fir");
retval retval
} }
pub fn firrtl_file(&self) -> PathBuf {
self.file_with_ext("fir")
}
} }
impl FirrtlArgs { impl FirrtlArgs {
fn run_impl( fn run_impl(&self, top_module: Module<Bundle>) -> Result<FirrtlOutput> {
&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 { let firrtl::FileBackend {
top_fir_file_stem, top_fir_file_stem, ..
circuit_name, } = firrtl::export(self.base.to_firrtl_file_backend(), &top_module)?;
dir_path,
} = firrtl::export(file_backend, &top_module, self.export_options)?;
Ok(FirrtlOutput { Ok(FirrtlOutput {
file_stem: top_fir_file_stem.expect( file_stem: top_fir_file_stem.expect(
"export is known to set the file stem from the circuit name if not provided", "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 { impl<T: BundleType> RunPhase<Module<T>> for FirrtlArgs {
type Output = FirrtlOutput; type Output = FirrtlOutput;
fn run_with_job( fn run(&self, top_module: Module<T>) -> Result<Self::Output> {
&self, self.run_impl(top_module.canonical())
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 { impl<T: BundleType> RunPhase<Interned<Module<T>>> for FirrtlArgs {
type Output = FirrtlOutput; type Output = FirrtlOutput;
fn run_with_job( fn run(&self, top_module: Interned<Module<T>>) -> Result<Self::Output> {
&self, self.run(*top_module)
top_module: Interned<Module<T>>,
acquired_job: &mut AcquiredJob,
) -> Result<Self::Output> {
self.run_with_job(*top_module, acquired_job)
} }
} }
@ -215,22 +120,7 @@ pub enum VerilogDialect {
Yosys, Yosys,
} }
impl fmt::Display for VerilogDialect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl VerilogDialect { 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] { pub fn firtool_extra_args(self) -> &'static [&'static str] {
match self { match self {
VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"], VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"],
@ -248,7 +138,7 @@ impl VerilogDialect {
} }
} }
#[derive(Parser, Debug, Clone)] #[derive(Args, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct VerilogArgs { pub struct VerilogArgs {
#[command(flatten)] #[command(flatten)]
@ -266,94 +156,39 @@ pub struct VerilogArgs {
/// adapt the generated Verilog for a particular toolchain /// adapt the generated Verilog for a particular toolchain
#[arg(long)] #[arg(long)]
pub verilog_dialect: Option<VerilogDialect>, pub verilog_dialect: Option<VerilogDialect>,
#[arg(long, short = 'g')]
pub debug: bool,
} }
#[derive(Debug)] #[derive(Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct VerilogOutput { pub struct VerilogOutput {
pub firrtl: FirrtlOutput, pub firrtl: FirrtlOutput,
pub verilog_files: Vec<PathBuf>,
pub contents_hash: Option<blake3::Hash>,
} }
impl VerilogOutput { impl VerilogOutput {
pub fn main_verilog_file(&self) -> PathBuf { pub fn verilog_file(&self, args: &VerilogArgs) -> PathBuf {
self.firrtl.file_with_ext("v") let mut retval = args.firrtl.base.output.join(&self.firrtl.file_stem);
} retval.set_extension("v");
fn unadjusted_verilog_file(&self) -> PathBuf { retval
self.firrtl.file_with_ext("unadjusted.v")
} }
} }
impl VerilogArgs { impl VerilogArgs {
fn process_unadjusted_verilog_file(&self, mut output: VerilogOutput) -> Result<VerilogOutput> { fn run_impl(&self, firrtl_output: FirrtlOutput) -> 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 { let output = VerilogOutput {
firrtl: firrtl_output, firrtl: firrtl_output,
verilog_files: vec![],
contents_hash: None,
}; };
let mut cmd = process::Command::new(firtool); let mut cmd = process::Command::new(&self.firtool);
cmd.arg(output.firrtl.firrtl_file()); cmd.arg(output.firrtl.firrtl_file(&self.firrtl));
cmd.arg("-o"); cmd.arg("-o");
cmd.arg(output.unadjusted_verilog_file()); cmd.arg(output.verilog_file(self));
if *debug { if let Some(dialect) = self.verilog_dialect {
cmd.arg("-g");
cmd.arg("--preserve-values=all");
}
if let Some(dialect) = verilog_dialect {
cmd.args(dialect.firtool_extra_args()); cmd.args(dialect.firtool_extra_args());
} }
cmd.args(firtool_extra_args); cmd.args(&self.firtool_extra_args);
cmd.current_dir(&output.firrtl.output_dir); cmd.current_dir(&self.firrtl.base.output);
let status = firrtl.base.run_external_command(acquired_job, cmd, None)?; let status = cmd.status()?;
if status.success() { if status.success() {
self.process_unadjusted_verilog_file(output) Ok(output)
} else { } else {
Err(CliError(eyre!( Err(CliError(eyre!(
"running {} failed: {status}", "running {} failed: {status}",
@ -368,316 +203,9 @@ where
FirrtlArgs: RunPhase<Arg, Output = FirrtlOutput>, FirrtlArgs: RunPhase<Arg, Output = FirrtlOutput>,
{ {
type Output = VerilogOutput; type Output = VerilogOutput;
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output> { fn run(&self, arg: Arg) -> Result<Self::Output> {
let firrtl_output = self.firrtl.run_with_job(arg, acquired_job)?; let firrtl_output = self.firrtl.run(arg)?;
self.run_impl(firrtl_output, acquired_job) self.run_impl(firrtl_output)
}
}
#[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)
}
}
#[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::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)
} }
} }
@ -687,8 +215,6 @@ enum CliCommand {
Firrtl(FirrtlArgs), Firrtl(FirrtlArgs),
/// Generate Verilog /// Generate Verilog
Verilog(VerilogArgs), Verilog(VerilogArgs),
/// Run a formal proof
Formal(FormalArgs),
} }
/// a simple CLI /// a simple CLI
@ -766,16 +292,13 @@ where
FirrtlArgs: RunPhase<T, Output = FirrtlOutput>, FirrtlArgs: RunPhase<T, Output = FirrtlOutput>,
{ {
type Output = (); type Output = ();
fn run_with_job(&self, arg: T, acquired_job: &mut AcquiredJob) -> Result<Self::Output> { fn run(&self, arg: T) -> Result<Self::Output> {
match &self.subcommand { match &self.subcommand {
CliCommand::Firrtl(c) => { CliCommand::Firrtl(c) => {
c.run_with_job(arg, acquired_job)?; c.run(arg)?;
} }
CliCommand::Verilog(c) => { CliCommand::Verilog(c) => {
c.run_with_job(arg, acquired_job)?; c.run(arg)?;
}
CliCommand::Formal(c) => {
c.run_with_job(arg, acquired_job)?;
} }
} }
Ok(()) Ok(())

View file

@ -4,7 +4,7 @@ use crate::{
expr::{Expr, ToExpr}, expr::{Expr, ToExpr},
hdl, hdl,
int::Bool, int::Bool,
reset::{Reset, ResetType}, reset::Reset,
source_location::SourceLocation, source_location::SourceLocation,
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
}; };
@ -88,9 +88,9 @@ impl ToClock for Expr<Clock> {
} }
#[hdl] #[hdl]
pub struct ClockDomain<R: ResetType = Reset> { pub struct ClockDomain {
pub clk: Clock, pub clk: Clock,
pub rst: R, pub rst: Reset,
} }
impl ToClock for bool { impl ToClock for bool {

View file

@ -2,22 +2,19 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{ expr::{ops::VariantAccess, Expr, ToExpr},
ops::{ExprPartialEq, VariantAccess},
Expr, ToExpr,
},
hdl, hdl,
int::Bool, int::Bool,
intern::{Intern, Interned}, intern::{Intern, Interned},
module::{ module::{
connect, enum_match_variants_helper, incomplete_wire, wire, enum_match_variants_helper, EnumMatchVariantAndInactiveScopeImpl,
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, EnumMatchVariantsIterImpl, Scope,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties}, ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties},
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{convert::Infallible, fmt, iter::FusedIterator}; use std::{fmt, iter::FusedIterator};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct EnumVariant { pub struct EnumVariant {
@ -172,12 +169,6 @@ impl EnumTypePropertiesBuilder {
} }
} }
impl Default for EnumTypePropertiesBuilder {
fn default() -> Self {
Self::new()
}
}
impl Enum { impl Enum {
#[track_caller] #[track_caller]
pub fn new(variants: Interned<[EnumVariant]>) -> Self { pub fn new(variants: Interned<[EnumVariant]>) -> Self {
@ -363,60 +354,6 @@ pub enum HdlOption<T: Type> {
HdlSome(T), HdlSome(T),
} }
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
#[hdl]
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_eq = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
HdlNone => connect(cmp_eq, false),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_eq, false),
HdlNone => connect(cmp_eq, true),
}
}
}
cmp_eq
}
#[hdl]
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_ne = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
HdlNone => connect(cmp_ne, true),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_ne, true),
HdlNone => connect(cmp_ne, false),
}
}
}
cmp_ne
}
}
#[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()
@ -427,307 +364,3 @@ pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
let value = value.to_expr(); let value = value.to_expr();
HdlOption[Expr::ty(value)].HdlSome(value) HdlOption[Expr::ty(value)].HdlSome(value)
} }
impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn try_map<R: Type, E>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Result<Expr<R>, E>,
) -> Result<Expr<HdlOption<R>>, E> {
Self::try_and_then(expr, |v| Ok(HdlSome(f(v)?)))
}
#[track_caller]
pub fn map<R: Type>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<HdlOption<R>> {
Self::and_then(expr, |v| HdlSome(f(v)))
}
#[hdl]
#[track_caller]
pub fn try_and_then<R: Type, E>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Result<Expr<HdlOption<R>>, E>,
) -> Result<Expr<HdlOption<R>>, E> {
// manually run match steps so we can extract the return type to construct HdlNone
type Wrap<T> = T;
#[hdl]
let mut and_then_out = incomplete_wire();
let mut iter = Self::match_variants(expr, SourceLocation::caller());
let none = iter.next().unwrap();
let some = iter.next().unwrap();
assert!(iter.next().is_none());
let (Wrap::<<Self as Type>::MatchVariant>::HdlSome(value), some_scope) =
Self::match_activate_scope(some)
else {
unreachable!();
};
let value = f(value).inspect_err(|_| {
and_then_out.complete(()); // avoid error
})?;
let and_then_out = and_then_out.complete(Expr::ty(value));
connect(and_then_out, value);
drop(some_scope);
let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) =
Self::match_activate_scope(none)
else {
unreachable!();
};
connect(and_then_out, Expr::ty(and_then_out).HdlNone());
drop(none_scope);
Ok(and_then_out)
}
#[track_caller]
pub fn and_then<R: Type>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Expr<HdlOption<R>>,
) -> Expr<HdlOption<R>> {
match Self::try_and_then(expr, |v| Ok::<_, Infallible>(f(v))) {
Ok(v) => v,
Err(e) => match e {},
}
}
#[hdl]
#[track_caller]
pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> {
#[hdl]
let and_out = wire(Expr::ty(opt_b));
connect(and_out, Expr::ty(opt_b).HdlNone());
#[hdl]
if let HdlSome(_) = expr {
connect(and_out, opt_b);
}
and_out
}
#[hdl]
#[track_caller]
pub fn try_filter<E>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>,
) -> Result<Expr<Self>, E> {
#[hdl]
let filtered = wire(Expr::ty(expr));
connect(filtered, Expr::ty(expr).HdlNone());
let mut f = Some(f);
#[hdl]
if let HdlSome(v) = expr {
#[hdl]
if f.take().unwrap()(v)? {
connect(filtered, HdlSome(v));
}
}
Ok(filtered)
}
#[hdl]
#[track_caller]
pub fn filter(expr: Expr<Self>, f: impl FnOnce(Expr<T>) -> Expr<Bool>) -> Expr<Self> {
match Self::try_filter(expr, |v| Ok::<_, Infallible>(f(v))) {
Ok(v) => v,
Err(e) => match e {},
}
}
#[hdl]
#[track_caller]
pub fn try_inspect<E>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Result<(), E>,
) -> Result<Expr<Self>, E> {
let mut f = Some(f);
#[hdl]
if let HdlSome(v) = expr {
f.take().unwrap()(v)?;
}
Ok(expr)
}
#[hdl]
#[track_caller]
pub fn inspect(expr: Expr<Self>, f: impl FnOnce(Expr<T>)) -> Expr<Self> {
let mut f = Some(f);
#[hdl]
if let HdlSome(v) = expr {
f.take().unwrap()(v);
}
expr
}
#[hdl]
#[track_caller]
pub fn is_none(expr: Expr<Self>) -> Expr<Bool> {
#[hdl]
let is_none_out: Bool = wire();
connect(is_none_out, false);
#[hdl]
if let HdlNone = expr {
connect(is_none_out, true);
}
is_none_out
}
#[hdl]
#[track_caller]
pub fn is_some(expr: Expr<Self>) -> Expr<Bool> {
#[hdl]
let is_some_out: Bool = wire();
connect(is_some_out, false);
#[hdl]
if let HdlSome(_) = expr {
connect(is_some_out, true);
}
is_some_out
}
#[hdl]
#[track_caller]
pub fn map_or<R: Type>(
expr: Expr<Self>,
default: Expr<R>,
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<R> {
#[hdl]
let mapped = wire(Expr::ty(default));
let mut f = Some(f);
#[hdl]
match expr {
HdlSome(v) => connect(mapped, f.take().unwrap()(v)),
HdlNone => connect(mapped, default),
}
mapped
}
#[hdl]
#[track_caller]
pub fn map_or_else<R: Type>(
expr: Expr<Self>,
default: impl FnOnce() -> Expr<R>,
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<R> {
#[hdl]
let mut mapped = incomplete_wire();
let mut default = Some(default);
let mut f = Some(f);
let mut retval = None;
#[hdl]
match expr {
HdlSome(v) => {
let v = f.take().unwrap()(v);
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
connect(mapped, v);
}
HdlNone => {
let v = default.take().unwrap()();
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
connect(mapped, v);
}
}
retval.unwrap()
}
#[hdl]
#[track_caller]
pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
#[hdl]
let or_out = wire(Expr::ty(expr));
connect(or_out, opt_b);
#[hdl]
if let HdlSome(_) = expr {
connect(or_out, expr);
}
or_out
}
#[hdl]
#[track_caller]
pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> {
#[hdl]
let or_else_out = wire(Expr::ty(expr));
connect(or_else_out, f());
#[hdl]
if let HdlSome(_) = expr {
connect(or_else_out, expr);
}
or_else_out
}
#[hdl]
#[track_caller]
pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> {
#[hdl]
let unwrap_or_else_out = wire(Expr::ty(default));
connect(unwrap_or_else_out, default);
#[hdl]
if let HdlSome(v) = expr {
connect(unwrap_or_else_out, v);
}
unwrap_or_else_out
}
#[hdl]
#[track_caller]
pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> {
#[hdl]
let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome);
connect(unwrap_or_else_out, f());
#[hdl]
if let HdlSome(v) = expr {
connect(unwrap_or_else_out, v);
}
unwrap_or_else_out
}
#[hdl]
#[track_caller]
pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
#[hdl]
let xor_out = wire(Expr::ty(expr));
#[hdl]
if let HdlSome(_) = expr {
#[hdl]
if let HdlNone = opt_b {
connect(xor_out, expr);
} else {
connect(xor_out, Expr::ty(expr).HdlNone());
}
} else {
connect(xor_out, opt_b);
}
xor_out
}
#[hdl]
#[track_caller]
pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> {
#[hdl]
let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]);
connect(zip_out, Expr::ty(zip_out).HdlNone());
#[hdl]
if let HdlSome(l) = expr {
#[hdl]
if let HdlSome(r) = other {
connect(zip_out, HdlSome((l, r)));
}
}
zip_out
}
}
impl<T: Type> HdlOption<HdlOption<T>> {
#[hdl]
#[track_caller]
pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> {
#[hdl]
let flattened = wire(Expr::ty(expr).HdlSome);
#[hdl]
match expr {
HdlSome(v) => connect(flattened, v),
HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()),
}
flattened
}
}
impl<T: Type, U: Type> HdlOption<(T, U)> {
#[hdl]
#[track_caller]
pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> {
let (t, u) = Expr::ty(expr).HdlSome;
#[hdl]
let unzipped = wire((HdlOption[t], HdlOption[u]));
connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone()));
#[hdl]
if let HdlSome(v) = expr {
connect(unzipped.0, HdlSome(v.0));
connect(unzipped.1, HdlSome(v.1));
}
unzipped
}
}

View file

@ -9,7 +9,7 @@ use crate::{
ops::ExprCastTo, ops::ExprCastTo,
target::{GetTarget, Target}, target::{GetTarget, Target},
}, },
int::{Bool, DynSize, IntType, SIntType, SIntValue, Size, SizeType, UInt, UIntType, UIntValue}, int::{Bool, DynSize, IntType, SIntType, SIntValue, Size, UInt, UIntType, UIntValue},
intern::{Intern, Interned}, intern::{Intern, Interned},
memory::{DynPortType, MemPort, PortType}, memory::{DynPortType, MemPort, PortType},
module::{ module::{
@ -17,7 +17,6 @@ use crate::{
Instance, ModuleIO, Instance, ModuleIO,
}, },
reg::Reg, reg::Reg,
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
ty::{CanonicalType, StaticType, Type, TypeWithDeref}, ty::{CanonicalType, StaticType, Type, TypeWithDeref},
wire::Wire, wire::Wire,
}; };
@ -112,7 +111,6 @@ expr_enum! {
BundleLiteral(ops::BundleLiteral), BundleLiteral(ops::BundleLiteral),
ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>), ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>),
EnumLiteral(ops::EnumLiteral), EnumLiteral(ops::EnumLiteral),
Uninit(ops::Uninit),
NotU(ops::NotU), NotU(ops::NotU),
NotS(ops::NotS), NotS(ops::NotS),
NotB(ops::NotB), NotB(ops::NotB),
@ -210,9 +208,7 @@ expr_enum! {
ModuleIO(ModuleIO<CanonicalType>), ModuleIO(ModuleIO<CanonicalType>),
Instance(Instance<Bundle>), Instance(Instance<Bundle>),
Wire(Wire<CanonicalType>), Wire(Wire<CanonicalType>),
Reg(Reg<CanonicalType, Reset>), Reg(Reg<CanonicalType>),
RegSync(Reg<CanonicalType, SyncReset>),
RegAsync(Reg<CanonicalType, AsyncReset>),
MemPort(MemPort<DynPortType>), MemPort(MemPort<DynPortType>),
} }
} }
@ -596,42 +592,25 @@ impl<T: Type> GetTarget for Wire<T> {
} }
} }
impl<T: Type, R: ResetType> ToExpr for Reg<T, R> { impl<T: Type> ToExpr for Reg<T> {
type Type = T; type Type = T;
fn to_expr(&self) -> Expr<Self::Type> { fn to_expr(&self) -> Expr<Self::Type> {
struct Dispatch;
impl ResetTypeDispatch for Dispatch {
type Input<T: ResetType> = Reg<CanonicalType, T>;
type Output<T: ResetType> = ExprEnum;
fn reset(self, input: Self::Input<Reset>) -> Self::Output<Reset> {
ExprEnum::Reg(input)
}
fn sync_reset(self, input: Self::Input<SyncReset>) -> Self::Output<SyncReset> {
ExprEnum::RegSync(input)
}
fn async_reset(self, input: Self::Input<AsyncReset>) -> Self::Output<AsyncReset> {
ExprEnum::RegAsync(input)
}
}
Expr { Expr {
__enum: R::dispatch(self.canonical(), Dispatch).intern_sized(), __enum: ExprEnum::Reg(self.canonical()).intern_sized(),
__ty: self.ty(), __ty: self.ty(),
__flow: self.flow(), __flow: self.flow(),
} }
} }
} }
impl<T: Type, R: ResetType> ToLiteralBits for Reg<T, R> { impl<T: Type> ToLiteralBits for Reg<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> { fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
Err(NotALiteralExpr) Err(NotALiteralExpr)
} }
} }
impl<T: Type, R: ResetType> GetTarget for Reg<T, R> { impl<T: Type> GetTarget for Reg<T> {
fn target(&self) -> Option<Interned<Target>> { fn target(&self) -> Option<Interned<Target>> {
Some(Intern::intern_sized(self.canonical().into())) Some(Intern::intern_sized(self.canonical().into()))
} }
@ -661,18 +640,6 @@ impl<T: PortType> GetTarget for MemPort<T> {
} }
} }
pub trait HdlPartialEq<Rhs> {
fn cmp_eq(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_ne(self, rhs: Rhs) -> Expr<Bool>;
}
pub trait HdlPartialOrd<Rhs>: HdlPartialEq<Rhs> {
fn cmp_lt(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_le(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_gt(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_ge(self, rhs: Rhs) -> Expr<Bool>;
}
pub trait ReduceBits { pub trait ReduceBits {
type UIntOutput; type UIntOutput;
type BoolOutput; type BoolOutput;
@ -730,28 +697,3 @@ pub fn check_match_expr<T: Type>(
_check_fn: impl FnOnce(T::MatchVariant, Infallible), _check_fn: impl FnOnce(T::MatchVariant, Infallible),
) { ) {
} }
pub trait MakeUninitExpr: Type {
fn uninit(self) -> Expr<Self>;
}
impl<T: Type> MakeUninitExpr for T {
fn uninit(self) -> Expr<Self> {
ops::Uninit::new(self).to_expr()
}
}
pub fn repeat<T: Type, L: SizeType>(
element: impl ToExpr<Type = T>,
len: L,
) -> Expr<ArrayType<T, L::Size>> {
let element = element.to_expr();
let canonical_element = Expr::canonical(element);
ops::ArrayLiteral::new(
Expr::ty(element),
std::iter::repeat(canonical_element)
.take(L::Size::as_usize(len))
.collect(),
)
.to_expr()
}

View file

@ -11,18 +11,14 @@ use crate::{
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, GetTarget, Target, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement, TargetPathDynArrayElement, TargetPathElement,
}, },
CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits, CastTo, Expr, ExprEnum, Flow, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits,
ToExpr, ToLiteralBits,
}, },
int::{ int::{
Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, Bool, BoolOrIntType, DynSize, IntCmp, IntType, KnownSize, SInt, SIntType, SIntValue, Size,
UIntType, UIntValue, UInt, UIntType, UIntValue,
}, },
intern::{Intern, Interned}, intern::{Intern, Interned},
reset::{ reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset,
ToSyncReset,
},
ty::{CanonicalType, StaticType, Type}, ty::{CanonicalType, StaticType, Type},
util::ConstUsize, util::ConstUsize,
}; };
@ -265,7 +261,7 @@ impl Neg {
}; };
let result_ty = retval.ty(); let result_ty = retval.ty();
retval.literal_bits = arg.to_literal_bits().map(|bits| { retval.literal_bits = arg.to_literal_bits().map(|bits| {
Intern::intern_owned(result_ty.bits_from_bigint_wrapping(&-SInt::bits_to_bigint(&bits))) Intern::intern_owned(result_ty.bits_from_bigint_wrapping(-SInt::bits_to_bigint(&bits)))
}); });
retval retval
} }
@ -372,7 +368,7 @@ fn binary_op_literal_bits<ResultTy: BoolOrIntType, Lhs: BoolOrIntType, Rhs: Bool
let rhs = Rhs::bits_to_bigint(&rhs); let rhs = Rhs::bits_to_bigint(&rhs);
let result = f(lhs, rhs)?; let result = f(lhs, rhs)?;
Ok(Intern::intern_owned( Ok(Intern::intern_owned(
result_ty.bits_from_bigint_wrapping(&result), result_ty.bits_from_bigint_wrapping(result),
)) ))
} }
@ -1240,11 +1236,10 @@ macro_rules! impl_dyn_shl {
} }
} }
impl<LhsWidth: Size, RhsWidth: Size> Shl<Expr<UIntType<RhsWidth>>> for Expr<$ty<LhsWidth>> { impl_binary_op_trait! {
type Output = Expr<$ty>; #[generics(LhsWidth: Size, RhsWidth: Size)]
fn Shl::shl(lhs: $ty<LhsWidth>, rhs: UIntType<RhsWidth>) -> $ty {
fn shl(self, rhs: Expr<UIntType<RhsWidth>>) -> Self::Output { $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr()
$name::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs)).to_expr()
} }
} }
}; };
@ -1313,11 +1308,10 @@ macro_rules! impl_dyn_shr {
} }
} }
impl<LhsWidth: Size, RhsWidth: Size> Shr<Expr<UIntType<RhsWidth>>> for Expr<$ty<LhsWidth>> { impl_binary_op_trait! {
type Output = Expr<$ty<LhsWidth>>; #[generics(LhsWidth: Size, RhsWidth: Size)]
fn Shr::shr(lhs: $ty<LhsWidth>, rhs: UIntType<RhsWidth>) -> $ty<LhsWidth> {
fn shr(self, rhs: Expr<UIntType<RhsWidth>>) -> Self::Output { $name::new(lhs, Expr::as_dyn_int(rhs)).to_expr()
$name::new(self, Expr::as_dyn_int(rhs)).to_expr()
} }
} }
}; };
@ -1347,7 +1341,7 @@ macro_rules! binary_op_fixed_shift {
literal_bits: Err(NotALiteralExpr), literal_bits: Err(NotALiteralExpr),
}; };
retval.literal_bits = lhs.to_literal_bits().map(|bits| { retval.literal_bits = lhs.to_literal_bits().map(|bits| {
Intern::intern_owned(retval.ty().bits_from_bigint_wrapping(&$Trait::$method( Intern::intern_owned(retval.ty().bits_from_bigint_wrapping($Trait::$method(
$ty::bits_to_bigint(&bits), $ty::bits_to_bigint(&bits),
rhs, rhs,
))) )))
@ -1426,45 +1420,36 @@ forward_value_to_expr_binary_op_trait! {
Shr::shr Shr::shr
} }
pub trait ExprPartialEq<Rhs: Type>: Type { pub trait IntCmpExpr<Rhs: Type>: Type {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>; fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>; fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
}
pub trait ExprPartialOrd<Rhs: Type>: ExprPartialEq<Rhs> {
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>; fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
fn cmp_le(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>; fn cmp_le(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>; fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>; fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
} }
impl<Lhs: ToExpr, Rhs: ToExpr> HdlPartialEq<Rhs> for Lhs impl<Lhs: ToExpr, Rhs: ToExpr> IntCmp<Rhs> for Lhs
where where
Lhs::Type: ExprPartialEq<Rhs::Type>, Lhs::Type: IntCmpExpr<Rhs::Type>,
{ {
fn cmp_eq(self, rhs: Rhs) -> Expr<Bool> { fn cmp_eq(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialEq::cmp_eq(self.to_expr(), rhs.to_expr()) IntCmpExpr::cmp_eq(self.to_expr(), rhs.to_expr())
} }
fn cmp_ne(self, rhs: Rhs) -> Expr<Bool> { fn cmp_ne(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialEq::cmp_ne(self.to_expr(), rhs.to_expr()) IntCmpExpr::cmp_ne(self.to_expr(), rhs.to_expr())
} }
}
impl<Lhs: ToExpr, Rhs: ToExpr> HdlPartialOrd<Rhs> for Lhs
where
Lhs::Type: ExprPartialOrd<Rhs::Type>,
{
fn cmp_lt(self, rhs: Rhs) -> Expr<Bool> { fn cmp_lt(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialOrd::cmp_lt(self.to_expr(), rhs.to_expr()) IntCmpExpr::cmp_lt(self.to_expr(), rhs.to_expr())
} }
fn cmp_le(self, rhs: Rhs) -> Expr<Bool> { fn cmp_le(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialOrd::cmp_le(self.to_expr(), rhs.to_expr()) IntCmpExpr::cmp_le(self.to_expr(), rhs.to_expr())
} }
fn cmp_gt(self, rhs: Rhs) -> Expr<Bool> { fn cmp_gt(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialOrd::cmp_gt(self.to_expr(), rhs.to_expr()) IntCmpExpr::cmp_gt(self.to_expr(), rhs.to_expr())
} }
fn cmp_ge(self, rhs: Rhs) -> Expr<Bool> { fn cmp_ge(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialOrd::cmp_ge(self.to_expr(), rhs.to_expr()) IntCmpExpr::cmp_ge(self.to_expr(), rhs.to_expr())
} }
} }
@ -1474,7 +1459,6 @@ macro_rules! impl_compare_op {
#[dyn_type($DynTy:ident)] #[dyn_type($DynTy:ident)]
#[to_dyn_type($lhs:ident => $dyn_lhs:expr, $rhs:ident => $dyn_rhs:expr)] #[to_dyn_type($lhs:ident => $dyn_lhs:expr, $rhs:ident => $dyn_rhs:expr)]
#[type($Lhs:ty, $Rhs:ty)] #[type($Lhs:ty, $Rhs:ty)]
#[trait($Trait:ident)]
$( $(
struct $name:ident; struct $name:ident;
fn $method:ident(); fn $method:ident();
@ -1526,7 +1510,7 @@ macro_rules! impl_compare_op {
} }
})* })*
impl$(<$LhsWidth: Size, $RhsWidth: Size>)? $Trait<$Rhs> for $Lhs { impl$(<$LhsWidth: Size, $RhsWidth: Size>)? IntCmpExpr<$Rhs> for $Lhs {
$(fn $method($lhs: Expr<Self>, $rhs: Expr<$Rhs>) -> Expr<Bool> { $(fn $method($lhs: Expr<Self>, $rhs: Expr<$Rhs>) -> Expr<Bool> {
$name::new($dyn_lhs, $dyn_rhs).to_expr() $name::new($dyn_lhs, $dyn_rhs).to_expr()
})* })*
@ -1538,16 +1522,8 @@ impl_compare_op! {
#[dyn_type(Bool)] #[dyn_type(Bool)]
#[to_dyn_type(lhs => lhs, rhs => rhs)] #[to_dyn_type(lhs => lhs, rhs => rhs)]
#[type(Bool, Bool)] #[type(Bool, Bool)]
#[trait(ExprPartialEq)]
struct CmpEqB; fn cmp_eq(); PartialEq::eq(); struct CmpEqB; fn cmp_eq(); PartialEq::eq();
struct CmpNeB; fn cmp_ne(); PartialEq::ne(); struct CmpNeB; fn cmp_ne(); PartialEq::ne();
}
impl_compare_op! {
#[dyn_type(Bool)]
#[to_dyn_type(lhs => lhs, rhs => rhs)]
#[type(Bool, Bool)]
#[trait(ExprPartialOrd)]
struct CmpLtB; fn cmp_lt(); PartialOrd::lt(); struct CmpLtB; fn cmp_lt(); PartialOrd::lt();
struct CmpLeB; fn cmp_le(); PartialOrd::le(); struct CmpLeB; fn cmp_le(); PartialOrd::le();
struct CmpGtB; fn cmp_gt(); PartialOrd::gt(); struct CmpGtB; fn cmp_gt(); PartialOrd::gt();
@ -1559,17 +1535,8 @@ impl_compare_op! {
#[dyn_type(UInt)] #[dyn_type(UInt)]
#[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))]
#[type(UIntType<LhsWidth>, UIntType<RhsWidth>)] #[type(UIntType<LhsWidth>, UIntType<RhsWidth>)]
#[trait(ExprPartialEq)]
struct CmpEqU; fn cmp_eq(); PartialEq::eq(); struct CmpEqU; fn cmp_eq(); PartialEq::eq();
struct CmpNeU; fn cmp_ne(); PartialEq::ne(); struct CmpNeU; fn cmp_ne(); PartialEq::ne();
}
impl_compare_op! {
#[width(LhsWidth, RhsWidth)]
#[dyn_type(UInt)]
#[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))]
#[type(UIntType<LhsWidth>, UIntType<RhsWidth>)]
#[trait(ExprPartialOrd)]
struct CmpLtU; fn cmp_lt(); PartialOrd::lt(); struct CmpLtU; fn cmp_lt(); PartialOrd::lt();
struct CmpLeU; fn cmp_le(); PartialOrd::le(); struct CmpLeU; fn cmp_le(); PartialOrd::le();
struct CmpGtU; fn cmp_gt(); PartialOrd::gt(); struct CmpGtU; fn cmp_gt(); PartialOrd::gt();
@ -1581,17 +1548,8 @@ impl_compare_op! {
#[dyn_type(SInt)] #[dyn_type(SInt)]
#[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))]
#[type(SIntType<LhsWidth>, SIntType<RhsWidth>)] #[type(SIntType<LhsWidth>, SIntType<RhsWidth>)]
#[trait(ExprPartialEq)]
struct CmpEqS; fn cmp_eq(); PartialEq::eq(); struct CmpEqS; fn cmp_eq(); PartialEq::eq();
struct CmpNeS; fn cmp_ne(); PartialEq::ne(); struct CmpNeS; fn cmp_ne(); PartialEq::ne();
}
impl_compare_op! {
#[width(LhsWidth, RhsWidth)]
#[dyn_type(SInt)]
#[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))]
#[type(SIntType<LhsWidth>, SIntType<RhsWidth>)]
#[trait(ExprPartialOrd)]
struct CmpLtS; fn cmp_lt(); PartialOrd::lt(); struct CmpLtS; fn cmp_lt(); PartialOrd::lt();
struct CmpLeS; fn cmp_le(); PartialOrd::le(); struct CmpLeS; fn cmp_le(); PartialOrd::le();
struct CmpGtS; fn cmp_gt(); PartialOrd::gt(); struct CmpGtS; fn cmp_gt(); PartialOrd::gt();
@ -1624,7 +1582,7 @@ macro_rules! impl_cast_int_op {
ty, ty,
literal_bits: arg.to_literal_bits().map(|bits| { literal_bits: arg.to_literal_bits().map(|bits| {
Intern::intern_owned( Intern::intern_owned(
ty.bits_from_bigint_wrapping(&$from::bits_to_bigint(&bits)), ty.bits_from_bigint_wrapping($from::bits_to_bigint(&bits)),
) )
}), }),
} }
@ -1776,11 +1734,11 @@ impl_cast_bit_op!(CastSIntToAsyncReset, SInt<1>, #[dyn] SInt, AsyncReset, #[trai
impl_cast_bit_op!(CastSyncResetToBool, SyncReset, Bool); impl_cast_bit_op!(CastSyncResetToBool, SyncReset, Bool);
impl_cast_bit_op!(CastSyncResetToUInt, SyncReset, UInt<1>, #[dyn] UInt); impl_cast_bit_op!(CastSyncResetToUInt, SyncReset, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastSyncResetToSInt, SyncReset, SInt<1>, #[dyn] SInt); impl_cast_bit_op!(CastSyncResetToSInt, SyncReset, SInt<1>, #[dyn] SInt);
impl_cast_bit_op!(CastSyncResetToReset, SyncReset, Reset); impl_cast_bit_op!(CastSyncResetToReset, SyncReset, Reset, #[trait] ToReset::to_reset);
impl_cast_bit_op!(CastAsyncResetToBool, AsyncReset, Bool); impl_cast_bit_op!(CastAsyncResetToBool, AsyncReset, Bool);
impl_cast_bit_op!(CastAsyncResetToUInt, AsyncReset, UInt<1>, #[dyn] UInt); impl_cast_bit_op!(CastAsyncResetToUInt, AsyncReset, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastAsyncResetToSInt, AsyncReset, SInt<1>, #[dyn] SInt); impl_cast_bit_op!(CastAsyncResetToSInt, AsyncReset, SInt<1>, #[dyn] SInt);
impl_cast_bit_op!(CastAsyncResetToReset, AsyncReset, Reset); impl_cast_bit_op!(CastAsyncResetToReset, AsyncReset, Reset, #[trait] ToReset::to_reset);
impl_cast_bit_op!(CastResetToBool, Reset, Bool); impl_cast_bit_op!(CastResetToBool, Reset, Bool);
impl_cast_bit_op!(CastResetToUInt, Reset, UInt<1>, #[dyn] UInt); impl_cast_bit_op!(CastResetToUInt, Reset, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastResetToSInt, Reset, SInt<1>, #[dyn] SInt); impl_cast_bit_op!(CastResetToSInt, Reset, SInt<1>, #[dyn] SInt);
@ -1791,107 +1749,6 @@ impl_cast_bit_op!(CastClockToBool, Clock, Bool);
impl_cast_bit_op!(CastClockToUInt, Clock, UInt<1>, #[dyn] UInt); impl_cast_bit_op!(CastClockToUInt, Clock, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastClockToSInt, Clock, SInt<1>, #[dyn] SInt); impl_cast_bit_op!(CastClockToSInt, Clock, SInt<1>, #[dyn] SInt);
impl<T: ResetType> ToReset for Expr<T> {
fn to_reset(&self) -> Expr<Reset> {
struct Dispatch;
impl ResetTypeDispatch for Dispatch {
type Input<T: ResetType> = Expr<T>;
type Output<T: ResetType> = Expr<Reset>;
fn reset(self, input: Self::Input<Reset>) -> Self::Output<Reset> {
input
}
fn sync_reset(self, input: Self::Input<SyncReset>) -> Self::Output<SyncReset> {
input.cast_to_static()
}
fn async_reset(self, input: Self::Input<AsyncReset>) -> Self::Output<AsyncReset> {
input.cast_to_static()
}
}
T::dispatch(*self, Dispatch)
}
}
impl ExprCastTo<AsyncReset> for AsyncReset {
fn cast_to(src: Expr<Self>, _to_type: AsyncReset) -> Expr<AsyncReset> {
src
}
}
impl ExprCastTo<SyncReset> for AsyncReset {
fn cast_to(src: Expr<Self>, to_type: SyncReset) -> Expr<SyncReset> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<Clock> for AsyncReset {
fn cast_to(src: Expr<Self>, to_type: Clock) -> Expr<Clock> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<AsyncReset> for SyncReset {
fn cast_to(src: Expr<Self>, to_type: AsyncReset) -> Expr<AsyncReset> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<SyncReset> for SyncReset {
fn cast_to(src: Expr<Self>, _to_type: SyncReset) -> Expr<SyncReset> {
src
}
}
impl ExprCastTo<Clock> for SyncReset {
fn cast_to(src: Expr<Self>, to_type: Clock) -> Expr<Clock> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<AsyncReset> for Reset {
fn cast_to(src: Expr<Self>, to_type: AsyncReset) -> Expr<AsyncReset> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<SyncReset> for Reset {
fn cast_to(src: Expr<Self>, to_type: SyncReset) -> Expr<SyncReset> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<Reset> for Reset {
fn cast_to(src: Expr<Self>, _to_type: Reset) -> Expr<Reset> {
src
}
}
impl ExprCastTo<Clock> for Reset {
fn cast_to(src: Expr<Self>, to_type: Clock) -> Expr<Clock> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<AsyncReset> for Clock {
fn cast_to(src: Expr<Self>, to_type: AsyncReset) -> Expr<AsyncReset> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<SyncReset> for Clock {
fn cast_to(src: Expr<Self>, to_type: SyncReset) -> Expr<SyncReset> {
src.cast_to(Bool).cast_to(to_type)
}
}
impl ExprCastTo<Clock> for Clock {
fn cast_to(src: Expr<Self>, _to_type: Clock) -> Expr<Clock> {
src
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct FieldAccess<FieldType: Type = CanonicalType> { pub struct FieldAccess<FieldType: Type = CanonicalType> {
base: Expr<Bundle>, base: Expr<Bundle>,
@ -2161,7 +2018,7 @@ impl<ElementType: Type, Len: Size> ExprIndex<usize> for ArrayType<ElementType, L
#[track_caller] #[track_caller]
fn expr_index(this: &Expr<Self>, index: usize) -> &Expr<Self::Output> { fn expr_index(this: &Expr<Self>, index: usize) -> &Expr<Self::Output> {
Interned::into_inner( Interned::<_>::into_inner(
ArrayIndex::<ElementType>::new(Expr::as_dyn_array(*this), index) ArrayIndex::<ElementType>::new(Expr::as_dyn_array(*this), index)
.to_expr() .to_expr()
.intern_sized(), .intern_sized(),
@ -2258,7 +2115,7 @@ impl<ElementType: Type, Len: Size, Width: Size> ExprIndex<Expr<UIntType<Width>>>
type Output = ElementType; type Output = ElementType;
fn expr_index(this: &Expr<Self>, index: Expr<UIntType<Width>>) -> &Expr<Self::Output> { fn expr_index(this: &Expr<Self>, index: Expr<UIntType<Width>>) -> &Expr<Self::Output> {
Interned::into_inner( Interned::<_>::into_inner(
DynArrayIndex::<ElementType>::new(Expr::as_dyn_array(*this), Expr::as_dyn_int(index)) DynArrayIndex::<ElementType>::new(Expr::as_dyn_array(*this), Expr::as_dyn_int(index))
.to_expr() .to_expr()
.intern_sized(), .intern_sized(),
@ -2383,7 +2240,7 @@ macro_rules! impl_int_slice {
let base = Expr::as_dyn_int(*this); let base = Expr::as_dyn_int(*this);
let base_ty = Expr::ty(base); let base_ty = Expr::ty(base);
let range = base_ty.slice_index_to_range(index); let range = base_ty.slice_index_to_range(index);
Interned::into_inner($name::new(base, range).to_expr().intern_sized()) Interned::<_>::into_inner($name::new(base, range).to_expr().intern_sized())
} }
} }
@ -2395,7 +2252,7 @@ macro_rules! impl_int_slice {
let base = Expr::as_dyn_int(*this); let base = Expr::as_dyn_int(*this);
let base_ty = Expr::ty(base); let base_ty = Expr::ty(base);
assert!(index < base_ty.width()); assert!(index < base_ty.width());
Interned::into_inner( Interned::<_>::into_inner(
$name::new(base, index..(index + 1)) $name::new(base, index..(index + 1))
.to_expr() .to_expr()
.cast_to_static::<Bool>() .cast_to_static::<Bool>()
@ -2670,41 +2527,3 @@ impl<T: Type> ToExpr for CastBitsTo<T> {
} }
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Uninit<T: Type = CanonicalType> {
ty: T,
}
impl<T: Type> Uninit<T> {
#[track_caller]
pub fn new(ty: T) -> Self {
Self { ty }
}
pub fn ty(self) -> T {
self.ty
}
}
impl<T: Type> ToLiteralBits for Uninit<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
Err(NotALiteralExpr)
}
}
impl_get_target_none!([T: Type] Uninit<T>);
impl<T: Type> ToExpr for Uninit<T> {
type Type = T;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::Uninit(Uninit {
ty: self.ty.canonical(),
})
.intern(),
__ty: self.ty,
__flow: Flow::Source,
}
}
}

View file

@ -1,21 +1,18 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{ use crate::{
array::Array, array::Array,
bundle::{Bundle, BundleField}, bundle::{Bundle, BundleField},
expr::{Expr, Flow, ToExpr}, expr::Flow,
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},
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
wire::Wire, wire::Wire,
}; };
use std::fmt; use std::fmt;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathBundleField { pub struct TargetPathBundleField {
pub name: Interned<str>, pub name: Interned<str>,
} }
@ -26,7 +23,7 @@ impl fmt::Display for TargetPathBundleField {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathArrayElement { pub struct TargetPathArrayElement {
pub index: usize, pub index: usize,
} }
@ -37,7 +34,7 @@ impl fmt::Display for TargetPathArrayElement {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathDynArrayElement {} pub struct TargetPathDynArrayElement {}
impl fmt::Display for TargetPathDynArrayElement { impl fmt::Display for TargetPathDynArrayElement {
@ -46,7 +43,7 @@ impl fmt::Display for TargetPathDynArrayElement {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TargetPathElement { pub enum TargetPathElement {
BundleField(TargetPathBundleField), BundleField(TargetPathBundleField),
ArrayElement(TargetPathArrayElement), ArrayElement(TargetPathArrayElement),
@ -128,7 +125,6 @@ macro_rules! impl_target_base {
$(#[$enum_meta:meta])* $(#[$enum_meta:meta])*
$enum_vis:vis enum $TargetBase:ident { $enum_vis:vis enum $TargetBase:ident {
$( $(
$(#[from = $from:ident])?
#[is = $is_fn:ident] #[is = $is_fn:ident]
#[to = $to_fn:ident] #[to = $to_fn:ident]
$(#[$variant_meta:meta])* $(#[$variant_meta:meta])*
@ -152,19 +148,19 @@ macro_rules! impl_target_base {
} }
} }
$($( $(
impl From<$VariantTy> for $TargetBase { impl From<$VariantTy> for $TargetBase {
fn $from(value: $VariantTy) -> Self { fn from(value: $VariantTy) -> Self {
Self::$Variant(value) Self::$Variant(value)
} }
} }
impl From<$VariantTy> for Target { impl From<$VariantTy> for Target {
fn $from(value: $VariantTy) -> Self { fn from(value: $VariantTy) -> Self {
$TargetBase::$Variant(value).into() $TargetBase::$Variant(value).into()
} }
} }
)*)? )*
impl $TargetBase { impl $TargetBase {
$( $(
@ -195,79 +191,30 @@ macro_rules! impl_target_base {
} }
} }
} }
impl ToExpr for $TargetBase {
type Type = CanonicalType;
fn to_expr(&self) -> Expr<Self::Type> {
match self {
$(Self::$Variant(v) => Expr::canonical(v.to_expr()),)*
}
}
}
}; };
} }
impl_target_base! { impl_target_base! {
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub enum TargetBase { pub enum TargetBase {
#[from = from]
#[is = is_module_io] #[is = is_module_io]
#[to = module_io] #[to = module_io]
ModuleIO(ModuleIO<CanonicalType>), ModuleIO(ModuleIO<CanonicalType>),
#[from = from]
#[is = is_mem_port] #[is = is_mem_port]
#[to = mem_port] #[to = mem_port]
MemPort(MemPort<DynPortType>), MemPort(MemPort<DynPortType>),
#[is = is_reg] #[is = is_reg]
#[to = reg] #[to = reg]
Reg(Reg<CanonicalType, Reset>), Reg(Reg<CanonicalType>),
#[is = is_reg_sync]
#[to = reg_sync]
RegSync(Reg<CanonicalType, SyncReset>),
#[is = is_reg_async]
#[to = reg_async]
RegAsync(Reg<CanonicalType, AsyncReset>),
#[from = from]
#[is = is_wire] #[is = is_wire]
#[to = wire] #[to = wire]
Wire(Wire<CanonicalType>), Wire(Wire<CanonicalType>),
#[from = from]
#[is = is_instance] #[is = is_instance]
#[to = instance] #[to = instance]
Instance(Instance<Bundle>), Instance(Instance<Bundle>),
} }
} }
impl<R: ResetType> From<Reg<CanonicalType, R>> for TargetBase {
fn from(value: Reg<CanonicalType, R>) -> Self {
struct Dispatch;
impl ResetTypeDispatch for Dispatch {
type Input<T: ResetType> = Reg<CanonicalType, T>;
type Output<T: ResetType> = TargetBase;
fn reset(self, input: Self::Input<Reset>) -> Self::Output<Reset> {
TargetBase::Reg(input)
}
fn sync_reset(self, input: Self::Input<SyncReset>) -> Self::Output<SyncReset> {
TargetBase::RegSync(input)
}
fn async_reset(self, input: Self::Input<AsyncReset>) -> Self::Output<AsyncReset> {
TargetBase::RegAsync(input)
}
}
R::dispatch(value, Dispatch)
}
}
impl<R: ResetType> From<Reg<CanonicalType, R>> for Target {
fn from(value: Reg<CanonicalType, R>) -> Self {
TargetBase::from(value).into()
}
}
impl fmt::Display for TargetBase { impl fmt::Display for TargetBase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.target_name()) write!(f, "{:?}", self.target_name())
@ -280,8 +227,6 @@ impl TargetBase {
TargetBase::ModuleIO(v) => TargetName(v.scoped_name(), None), TargetBase::ModuleIO(v) => TargetName(v.scoped_name(), None),
TargetBase::MemPort(v) => TargetName(v.mem_name(), Some(v.port_name())), TargetBase::MemPort(v) => TargetName(v.mem_name(), Some(v.port_name())),
TargetBase::Reg(v) => TargetName(v.scoped_name(), None), TargetBase::Reg(v) => TargetName(v.scoped_name(), None),
TargetBase::RegSync(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),
} }
@ -291,8 +236,6 @@ impl TargetBase {
TargetBase::ModuleIO(v) => v.ty(), TargetBase::ModuleIO(v) => v.ty(),
TargetBase::MemPort(v) => v.ty().canonical(), TargetBase::MemPort(v) => v.ty().canonical(),
TargetBase::Reg(v) => v.ty(), TargetBase::Reg(v) => v.ty(),
TargetBase::RegSync(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(),
} }
@ -368,7 +311,7 @@ impl TargetChild {
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub enum Target { pub enum Target {
Base(Interned<TargetBase>), Base(Interned<TargetBase>),
Child(TargetChild), Child(TargetChild),

View file

@ -2,10 +2,7 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use crate::{ use crate::{
annotations::{ annotations::CustomFirrtlAnnotation,
Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
},
array::Array, array::Array,
bundle::{Bundle, BundleField, BundleType}, bundle::{Bundle, BundleField, BundleType},
clock::Clock, clock::Clock,
@ -17,21 +14,16 @@ use crate::{
}, },
Expr, ExprEnum, Expr, ExprEnum,
}, },
formal::FormalKind,
int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue},
intern::{Intern, Interned}, intern::{Intern, Interned},
memory::{Mem, PortKind, PortName, ReadUnderWrite}, memory::{Mem, PortKind, PortName, ReadUnderWrite},
module::{ module::{
transform::{ transform::simplify_memories::simplify_memories, AnnotatedModuleIO, Block,
simplify_enums::{simplify_enums, SimplifyEnumsError, SimplifyEnumsKind}, ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Module, ModuleBody,
simplify_memories::simplify_memories, NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtIf, StmtInstance,
}, StmtMatch, StmtReg, StmtWire,
AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter,
ExternModuleParameterValue, Module, ModuleBody, NameOptId, NormalModuleBody, Stmt,
StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg,
StmtWire,
}, },
reset::{AsyncReset, Reset, ResetType, SyncReset}, reset::{AsyncReset, Reset, SyncReset},
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
util::{ util::{
@ -40,7 +32,6 @@ use crate::{
}, },
}; };
use bitvec::slice::BitSlice; use bitvec::slice::BitSlice;
use clap::value_parser;
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use num_traits::Signed; use num_traits::Signed;
use serde::Serialize; use serde::Serialize;
@ -187,9 +178,9 @@ struct NameMaker {
} }
impl NameMaker { impl NameMaker {
fn make(&mut self, name: impl Into<String>) -> Ident { fn make(&mut self, name: NameId) -> Ident {
let mut num = 0usize; let mut num: usize = name.1;
let name: String = name.into(); let name = String::from(&*name.0);
// remove all invalid characters -- all valid characters are ASCII, so we can just remove invalid bytes // remove all invalid characters -- all valid characters are ASCII, so we can just remove invalid bytes
let mut name = String::from_iter( let mut name = String::from_iter(
name.bytes() name.bytes()
@ -221,7 +212,7 @@ impl NameMaker {
#[derive(Default)] #[derive(Default)]
struct Namespace { struct Namespace {
name_maker: NameMaker, name_maker: NameMaker,
map: HashMap<NameOptId, Ident>, map: HashMap<NameId, Ident>,
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@ -247,11 +238,10 @@ impl From<PortName> for Ident {
} }
impl Namespace { impl Namespace {
fn get(&mut self, name: impl Into<NameOptId>) -> Ident { fn get(&mut self, name: NameId) -> Ident {
let name: NameOptId = name.into();
#[cold] #[cold]
fn make(name_maker: &mut NameMaker, name: NameOptId) -> Ident { fn make(name_maker: &mut NameMaker, name: NameId) -> Ident {
name_maker.make(name.0) name_maker.make(name)
} }
*self *self
.map .map
@ -259,7 +249,7 @@ impl Namespace {
.or_insert_with(|| make(&mut self.name_maker, name)) .or_insert_with(|| make(&mut self.name_maker, name))
} }
fn make_new(&mut self, prefix: &str) -> Ident { fn make_new(&mut self, prefix: &str) -> Ident {
self.name_maker.make(prefix) self.name_maker.make(NameId(prefix.intern(), 0))
} }
} }
@ -369,7 +359,7 @@ impl TypeState {
Ident(Intern::intern_owned(format!("Ty{id}"))) Ident(Intern::intern_owned(format!("Ty{id}")))
} }
fn get_bundle_field(&mut self, ty: Bundle, name: Interned<str>) -> Ident { fn get_bundle_field(&mut self, ty: Bundle, name: Interned<str>) -> Ident {
self.bundle_ns(ty).borrow_mut().get(name) self.bundle_ns(ty).borrow_mut().get(NameId(name, 0))
} }
fn bundle_def(&self, ty: Bundle) -> (Ident, Rc<RefCell<Namespace>>) { fn bundle_def(&self, ty: Bundle) -> (Ident, Rc<RefCell<Namespace>>) {
self.bundle_defs.get_or_make(ty, |&ty, definitions| { self.bundle_defs.get_or_make(ty, |&ty, definitions| {
@ -383,7 +373,7 @@ impl TypeState {
if flipped { if flipped {
body.push_str("flip "); body.push_str("flip ");
} }
write!(body, "{}: ", ns.get(name)).unwrap(); write!(body, "{}: ", ns.get(NameId(name, 0))).unwrap();
body.push_str(&self.ty(ty)); body.push_str(&self.ty(ty));
} }
body.push('}'); body.push('}');
@ -407,7 +397,7 @@ impl TypeState {
for EnumVariant { name, ty } in ty.variants() { for EnumVariant { name, ty } in ty.variants() {
body.push_str(separator); body.push_str(separator);
separator = ", "; separator = ", ";
write!(body, "{}", variants.get(name)).unwrap(); write!(body, "{}", variants.get(NameId(name, 0))).unwrap();
if let Some(ty) = ty { if let Some(ty) = ty {
body.push_str(": "); body.push_str(": ");
body.push_str(&self.ty(ty)); body.push_str(&self.ty(ty));
@ -429,7 +419,11 @@ impl TypeState {
self.enum_def(ty).0 self.enum_def(ty).0
} }
fn get_enum_variant(&mut self, ty: Enum, name: Interned<str>) -> Ident { fn get_enum_variant(&mut self, ty: Enum, name: Interned<str>) -> Ident {
self.enum_def(ty).1.variants.borrow_mut().get(name) self.enum_def(ty)
.1
.variants
.borrow_mut()
.get(NameId(name, 0))
} }
fn ty<T: Type>(&self, ty: T) -> String { fn ty<T: Type>(&self, ty: T) -> String {
match ty.canonical() { match ty.canonical() {
@ -482,7 +476,6 @@ trait WrappedFileBackendTrait {
circuit_name: String, circuit_name: String,
contents: String, contents: String,
) -> Result<(), WrappedError>; ) -> Result<(), WrappedError>;
fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError;
} }
struct WrappedFileBackend<B: FileBackendTrait> { struct WrappedFileBackend<B: FileBackendTrait> {
@ -540,11 +533,6 @@ impl<B: FileBackendTrait> WrappedFileBackendTrait for WrappedFileBackend<B> {
WrappedError WrappedError
}) })
} }
fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError {
self.error = Err(error.into());
WrappedError
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -666,17 +654,6 @@ enum AnnotationData {
}, },
#[serde(rename = "firrtl.transforms.DontTouchAnnotation")] #[serde(rename = "firrtl.transforms.DontTouchAnnotation")]
DontTouch, DontTouch,
#[serde(rename = "firrtl.AttributeAnnotation")]
AttributeAnnotation { description: Interned<str> },
#[serde(rename = "firrtl.transforms.BlackBoxInlineAnno")]
BlackBoxInlineAnno {
name: Interned<str>,
text: Interned<str>,
},
#[serde(rename = "firrtl.transforms.BlackBoxPathAnno")]
BlackBoxPathAnno { path: Interned<str> },
#[serde(rename = "firrtl.DocStringAnnotation")]
DocStringAnnotation { description: Interned<str> },
#[allow(dead_code)] #[allow(dead_code)]
#[serde(untagged)] #[serde(untagged)]
Other { Other {
@ -687,7 +664,7 @@ enum AnnotationData {
} }
#[derive(Serialize)] #[derive(Serialize)]
struct FirrtlAnnotation { struct Annotation {
#[serde(flatten)] #[serde(flatten)]
data: AnnotationData, data: AnnotationData,
target: AnnotationTarget, target: AnnotationTarget,
@ -702,7 +679,7 @@ struct Exporter<'a> {
module: ModuleState, module: ModuleState,
type_state: TypeState, type_state: TypeState,
circuit_name: Ident, circuit_name: Ident,
annotations: Vec<FirrtlAnnotation>, annotations: Vec<Annotation>,
} }
struct PushIndent<'a> { struct PushIndent<'a> {
@ -926,7 +903,7 @@ impl<'a> Exporter<'a> {
) in expr.field_values().into_iter().zip(ty.fields()) ) in expr.field_values().into_iter().zip(ty.fields())
{ {
debug_assert!(!flipped, "can't have bundle literal with flipped field -- this should have been caught in BundleLiteral::new_unchecked"); debug_assert!(!flipped, "can't have bundle literal with flipped field -- this should have been caught in BundleLiteral::new_unchecked");
let name = bundle_ns.borrow_mut().get(name); let name = bundle_ns.borrow_mut().get(NameId(name, 0));
let field_value = self.expr(Expr::canonical(field_value), definitions, const_ty); let field_value = self.expr(Expr::canonical(field_value), definitions, const_ty);
definitions.add_definition_line(format_args!("connect {ident}.{name}, {field_value}")); definitions.add_definition_line(format_args!("connect {ident}.{name}, {field_value}"));
} }
@ -935,20 +912,6 @@ impl<'a> Exporter<'a> {
} }
ident.to_string() ident.to_string()
} }
fn uninit_expr(
&mut self,
expr: ops::Uninit,
definitions: &RcDefinitions,
const_ty: bool,
) -> String {
let ident = self.module.ns.make_new("_uninit_expr");
let ty = expr.ty();
let ty_ident = self.type_state.ty(ty);
let const_ = if const_ty { "const " } else { "" };
definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_ident}"));
definitions.add_definition_line(format_args!("invalidate {ident}"));
ident.to_string()
}
fn enum_literal_expr( fn enum_literal_expr(
&mut self, &mut self,
expr: ops::EnumLiteral<Enum>, expr: ops::EnumLiteral<Enum>,
@ -1404,7 +1367,6 @@ impl<'a> Exporter<'a> {
ExprEnum::EnumLiteral(enum_literal) => { ExprEnum::EnumLiteral(enum_literal) => {
self.enum_literal_expr(enum_literal, definitions, const_ty) self.enum_literal_expr(enum_literal, definitions, const_ty)
} }
ExprEnum::Uninit(uninit) => self.uninit_expr(uninit, definitions, const_ty),
ExprEnum::NotU(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), ExprEnum::NotU(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty),
ExprEnum::NotS(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), ExprEnum::NotS(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty),
ExprEnum::NotB(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), ExprEnum::NotB(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty),
@ -1739,14 +1701,6 @@ impl<'a> Exporter<'a> {
assert!(!const_ty, "not a constant"); assert!(!const_ty, "not a constant");
self.module.ns.get(expr.scoped_name().1).to_string() self.module.ns.get(expr.scoped_name().1).to_string()
} }
ExprEnum::RegSync(expr) => {
assert!(!const_ty, "not a constant");
self.module.ns.get(expr.scoped_name().1).to_string()
}
ExprEnum::RegAsync(expr) => {
assert!(!const_ty, "not a constant");
self.module.ns.get(expr.scoped_name().1).to_string()
}
ExprEnum::MemPort(expr) => { ExprEnum::MemPort(expr) => {
assert!(!const_ty, "not a constant"); assert!(!const_ty, "not a constant");
let mem_name = self.module.ns.get(expr.mem_name().1); let mem_name = self.module.ns.get(expr.mem_name().1);
@ -1790,7 +1744,7 @@ impl<'a> Exporter<'a> {
memory_name.0.to_string(), memory_name.0.to_string(),
contents, contents,
)?; )?;
self.annotations.push(FirrtlAnnotation { self.annotations.push(Annotation {
data: AnnotationData::MemoryFileInline { data: AnnotationData::MemoryFileInline {
filename, filename,
hex_or_binary, hex_or_binary,
@ -1809,25 +1763,14 @@ impl<'a> Exporter<'a> {
}); });
Ok(()) Ok(())
} }
fn annotation(&mut self, path: AnnotationTargetPath, annotation: &Annotation) { fn annotation(
&mut self,
path: AnnotationTargetPath,
annotation: &crate::annotations::Annotation,
) {
let data = match annotation { let data = match annotation {
Annotation::DontTouch(DontTouchAnnotation {}) => AnnotationData::DontTouch, crate::annotations::Annotation::DontTouch => AnnotationData::DontTouch,
Annotation::SVAttribute(SVAttributeAnnotation { text }) => { crate::annotations::Annotation::CustomFirrtl(CustomFirrtlAnnotation {
AnnotationData::AttributeAnnotation { description: *text }
}
Annotation::BlackBoxInline(BlackBoxInlineAnnotation { path, text }) => {
AnnotationData::BlackBoxInlineAnno {
name: *path,
text: *text,
}
}
Annotation::BlackBoxPath(BlackBoxPathAnnotation { path }) => {
AnnotationData::BlackBoxPathAnno { path: *path }
}
Annotation::DocString(DocStringAnnotation { text }) => {
AnnotationData::DocStringAnnotation { description: *text }
}
Annotation::CustomFirrtl(CustomFirrtlAnnotation {
class, class,
additional_fields, additional_fields,
}) => AnnotationData::Other { }) => AnnotationData::Other {
@ -1835,7 +1778,7 @@ impl<'a> Exporter<'a> {
additional_fields: (*additional_fields).into(), additional_fields: (*additional_fields).into(),
}, },
}; };
self.annotations.push(FirrtlAnnotation { self.annotations.push(Annotation {
data, data,
target: AnnotationTarget { target: AnnotationTarget {
circuit: self.circuit_name, circuit: self.circuit_name,
@ -1856,8 +1799,6 @@ impl<'a> Exporter<'a> {
self.module.ns.get(v.mem_name().1) self.module.ns.get(v.mem_name().1)
} }
TargetBase::Reg(v) => self.module.ns.get(v.name_id()), TargetBase::Reg(v) => self.module.ns.get(v.name_id()),
TargetBase::RegSync(v) => self.module.ns.get(v.name_id()),
TargetBase::RegAsync(v) => self.module.ns.get(v.name_id()),
TargetBase::Wire(v) => self.module.ns.get(v.name_id()), TargetBase::Wire(v) => self.module.ns.get(v.name_id()),
TargetBase::Instance(v) => self.module.ns.get(v.name_id()), TargetBase::Instance(v) => self.module.ns.get(v.name_id()),
}; };
@ -1966,37 +1907,6 @@ impl<'a> Exporter<'a> {
drop(memory_indent); drop(memory_indent);
Ok(body) Ok(body)
} }
fn stmt_reg<R: ResetType>(
&mut self,
stmt_reg: StmtReg<R>,
module_name: Ident,
definitions: &RcDefinitions,
body: &mut String,
) {
let StmtReg { annotations, reg } = stmt_reg;
let indent = self.indent;
self.targeted_annotations(module_name, vec![], &annotations);
let name = self.module.ns.get(reg.name_id());
let ty = self.type_state.ty(reg.ty());
let clk = self.expr(Expr::canonical(reg.clock_domain().clk), definitions, false);
if let Some(init) = reg.init() {
let rst = self.expr(Expr::canonical(reg.clock_domain().rst), definitions, false);
let init = self.expr(init, definitions, false);
writeln!(
body,
"{indent}regreset {name}: {ty}, {clk}, {rst}, {init}{}",
FileInfo::new(reg.source_location()),
)
.unwrap();
} else {
writeln!(
body,
"{indent}reg {name}: {ty}, {clk}{}",
FileInfo::new(reg.source_location()),
)
.unwrap();
}
}
fn block( fn block(
&mut self, &mut self,
module: Interned<Module<Bundle>>, module: Interned<Module<Bundle>>,
@ -2020,15 +1930,6 @@ impl<'a> Exporter<'a> {
rhs, rhs,
source_location, source_location,
}) => { }) => {
if Expr::ty(lhs) != Expr::ty(rhs) {
writeln!(
body,
"{indent}; connect different types:\n{indent}; lhs: {:?}\n{indent}; rhs: {:?}",
Expr::ty(lhs),
Expr::ty(rhs),
)
.unwrap();
}
let lhs = self.expr(lhs, &definitions, false); let lhs = self.expr(lhs, &definitions, false);
let rhs = self.expr(rhs, &definitions, false); let rhs = self.expr(rhs, &definitions, false);
writeln!( writeln!(
@ -2038,33 +1939,6 @@ impl<'a> Exporter<'a> {
) )
.unwrap(); .unwrap();
} }
Stmt::Formal(StmtFormal {
kind,
clk,
pred,
en,
text,
source_location,
}) => {
let clk = self.expr(Expr::canonical(clk), &definitions, false);
let pred = self.expr(Expr::canonical(pred), &definitions, false);
let en = self.expr(Expr::canonical(en), &definitions, false);
let kind = match kind {
FormalKind::Assert => "assert",
FormalKind::Assume => "assume",
FormalKind::Cover => "cover",
};
let text = EscapedString {
value: &text,
raw: false,
};
writeln!(
body,
"{indent}{kind}({clk}, {pred}, {en}, {text}){}",
FileInfo::new(source_location),
)
.unwrap();
}
Stmt::If(StmtIf { Stmt::If(StmtIf {
mut cond, mut cond,
mut source_location, mut source_location,
@ -2167,14 +2041,30 @@ impl<'a> Exporter<'a> {
) )
.unwrap(); .unwrap();
} }
Stmt::Declaration(StmtDeclaration::Reg(stmt_reg)) => { Stmt::Declaration(StmtDeclaration::Reg(StmtReg { annotations, reg })) => {
self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); self.targeted_annotations(module_name, vec![], &annotations);
} let name = self.module.ns.get(reg.name_id());
Stmt::Declaration(StmtDeclaration::RegSync(stmt_reg)) => { let ty = self.type_state.ty(reg.ty());
self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); let clk =
} self.expr(Expr::canonical(reg.clock_domain().clk), &definitions, false);
Stmt::Declaration(StmtDeclaration::RegAsync(stmt_reg)) => { if let Some(init) = reg.init() {
self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); let rst =
self.expr(Expr::canonical(reg.clock_domain().rst), &definitions, false);
let init = self.expr(init, &definitions, false);
writeln!(
body,
"{indent}regreset {name}: {ty}, {clk}, {rst}, {init}{}",
FileInfo::new(reg.source_location()),
)
.unwrap();
} else {
writeln!(
body,
"{indent}reg {name}: {ty}, {clk}{}",
FileInfo::new(reg.source_location()),
)
.unwrap();
}
} }
Stmt::Declaration(StmtDeclaration::Instance(StmtInstance { Stmt::Declaration(StmtDeclaration::Instance(StmtInstance {
annotations, annotations,
@ -2221,7 +2111,7 @@ impl<'a> Exporter<'a> {
} in module.module_io().iter() } in module.module_io().iter()
{ {
self.targeted_annotations(module_name, vec![], annotations); self.targeted_annotations(module_name, vec![], annotations);
let name = self.module.ns.get(module_io.name_id()); let name = self.module.ns.get(NameId(module_io.name(), 0));
let ty = self.type_state.ty(module_io.ty()); let ty = self.type_state.ty(module_io.ty());
if module_io.is_input() { if module_io.is_input() {
writeln!( writeln!(
@ -2293,7 +2183,7 @@ impl<'a> Exporter<'a> {
} }
pub trait FileBackendTrait { pub trait FileBackendTrait {
type Error: From<SimplifyEnumsError>; type Error;
type Path: AsRef<Self::Path> + fmt::Debug + ?Sized; type Path: AsRef<Self::Path> + fmt::Debug + ?Sized;
type PathBuf: AsRef<Self::Path> + fmt::Debug; type PathBuf: AsRef<Self::Path> + fmt::Debug;
fn path_to_string(&mut self, path: &Self::Path) -> Result<String, Self::Error>; fn path_to_string(&mut self, path: &Self::Path) -> Result<String, Self::Error>;
@ -2368,7 +2258,6 @@ impl<T: ?Sized + FileBackendTrait> FileBackendTrait for &'_ mut T {
#[non_exhaustive] #[non_exhaustive]
pub struct FileBackend { pub struct FileBackend {
pub dir_path: PathBuf, pub dir_path: PathBuf,
pub circuit_name: Option<String>,
pub top_fir_file_stem: Option<String>, pub top_fir_file_stem: Option<String>,
} }
@ -2376,7 +2265,6 @@ impl FileBackend {
pub fn new(dir_path: impl AsRef<Path>) -> Self { pub fn new(dir_path: impl AsRef<Path>) -> Self {
Self { Self {
dir_path: dir_path.as_ref().to_owned(), dir_path: dir_path.as_ref().to_owned(),
circuit_name: None,
top_fir_file_stem: None, top_fir_file_stem: None,
} }
} }
@ -2412,10 +2300,7 @@ impl FileBackendTrait for FileBackend {
circuit_name: String, circuit_name: String,
contents: String, contents: String,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let top_fir_file_stem = self let top_fir_file_stem = self.top_fir_file_stem.get_or_insert(circuit_name);
.top_fir_file_stem
.get_or_insert_with(|| circuit_name.clone());
self.circuit_name = Some(circuit_name);
let mut path = self.dir_path.join(top_fir_file_stem); let mut path = self.dir_path.join(top_fir_file_stem);
if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) { if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) {
fs::create_dir_all(parent)?; fs::create_dir_all(parent)?;
@ -2426,17 +2311,15 @@ impl FileBackendTrait for FileBackend {
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq)]
pub struct TestBackendPrivate { pub struct TestBackendPrivate {
pub module_var_name: &'static str, pub module_var_name: &'static str,
pub included_fields: &'static [&'static str],
} }
impl Default for TestBackendPrivate { impl Default for TestBackendPrivate {
fn default() -> Self { fn default() -> Self {
Self { Self {
module_var_name: "m", module_var_name: "m",
included_fields: &[],
} }
} }
} }
@ -2445,7 +2328,6 @@ impl Default for TestBackendPrivate {
pub struct TestBackend { pub struct TestBackend {
pub files: BTreeMap<String, String>, pub files: BTreeMap<String, String>,
pub error_after: Option<i64>, pub error_after: Option<i64>,
pub options: ExportOptions,
#[doc(hidden)] #[doc(hidden)]
/// `#[non_exhaustive]` except allowing struct update syntax /// `#[non_exhaustive]` except allowing struct update syntax
pub __private: TestBackendPrivate, pub __private: TestBackendPrivate,
@ -2456,12 +2338,7 @@ impl fmt::Debug for TestBackend {
let Self { let Self {
files, files,
error_after, error_after,
options, __private: TestBackendPrivate { module_var_name },
__private:
TestBackendPrivate {
module_var_name,
included_fields,
},
} = self; } = self;
writeln!( writeln!(
f, f,
@ -2469,44 +2346,12 @@ impl fmt::Debug for TestBackend {
)?; )?;
writeln!(f, " assert_export_firrtl! {{")?; writeln!(f, " assert_export_firrtl! {{")?;
writeln!(f, " {module_var_name} =>")?; writeln!(f, " {module_var_name} =>")?;
if *error_after != Option::default() || included_fields.contains(&"error_after") {
writeln!(f, " error_after: {error_after:?},")?;
}
if *options != ExportOptions::default() || included_fields.contains(&"options") {
struct DebugWithForceIncludeFields<'a> {
options: ExportOptions,
included_fields: &'a [&'a str],
}
impl fmt::Debug for DebugWithForceIncludeFields<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.options.debug_fmt(f, |field| {
self.included_fields.iter().any(|included_field| {
if let Some(("options", suffix)) = included_field.split_once(".") {
suffix == field
} else {
false
}
})
})
}
}
let options_str = format!(
"{:#?}",
DebugWithForceIncludeFields {
options: *options,
included_fields
}
);
let mut sep = " options: ";
for line in options_str.lines() {
write!(f, "{sep}{line}")?;
sep = "\n ";
}
writeln!(f, ",")?;
}
for (file, content) in files { for (file, content) in files {
writeln!(f, " {file:?}: {:?},", DebugAsRawString(content))?; writeln!(f, " {file:?}: {:?},", DebugAsRawString(content))?;
} }
if *error_after != Option::default() {
writeln!(f, " error_after: {error_after:?},")?;
}
write!(f, " }};") write!(f, " }};")
} }
} }
@ -2522,12 +2367,6 @@ impl fmt::Display for TestBackendError {
impl Error for TestBackendError {} impl Error for TestBackendError {}
impl From<SimplifyEnumsError> for TestBackendError {
fn from(value: SimplifyEnumsError) -> Self {
TestBackendError(value.to_string())
}
}
impl TestBackend { impl TestBackend {
#[track_caller] #[track_caller]
pub fn step_error_after(&mut self, args: &dyn fmt::Debug) -> Result<(), TestBackendError> { pub fn step_error_after(&mut self, args: &dyn fmt::Debug) -> Result<(), TestBackendError> {
@ -2584,21 +2423,9 @@ impl FileBackendTrait for TestBackend {
fn export_impl( fn export_impl(
file_backend: &mut dyn WrappedFileBackendTrait, file_backend: &mut dyn WrappedFileBackendTrait,
mut top_module: Interned<Module<Bundle>>, top_module: Interned<Module<Bundle>>,
options: ExportOptions,
) -> Result<(), WrappedError> { ) -> Result<(), WrappedError> {
let ExportOptions { let top_module = simplify_memories(top_module);
simplify_memories: do_simplify_memories,
simplify_enums: do_simplify_enums,
__private: _,
} = options;
if let Some(kind) = do_simplify_enums {
top_module =
simplify_enums(top_module, kind).map_err(|e| file_backend.simplify_enums_error(e))?;
}
if do_simplify_memories {
top_module = simplify_memories(top_module);
}
let indent_depth = Cell::new(0); let indent_depth = Cell::new(0);
let mut global_ns = Namespace::default(); let mut global_ns = Namespace::default();
let circuit_name = global_ns.get(top_module.name_id()); let circuit_name = global_ns.get(top_module.name_id());
@ -2619,154 +2446,20 @@ fn export_impl(
.run(top_module) .run(top_module)
} }
#[derive(Clone)]
struct OptionSimplifyEnumsKindValueParser;
impl OptionSimplifyEnumsKindValueParser {
const NONE_NAME: &'static str = "off";
}
impl clap::builder::TypedValueParser for OptionSimplifyEnumsKindValueParser {
type Value = Option<SimplifyEnumsKind>;
fn parse_ref(
&self,
cmd: &clap::Command,
arg: Option<&clap::Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> {
if value == Self::NONE_NAME {
Ok(None)
} else {
Ok(Some(
value_parser!(SimplifyEnumsKind).parse_ref(cmd, arg, value)?,
))
}
}
fn possible_values(
&self,
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
Some(Box::new(
[Self::NONE_NAME.into()]
.into_iter()
.chain(value_parser!(SimplifyEnumsKind).possible_values()?)
.collect::<Vec<_>>()
.into_iter(),
))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ExportOptionsPrivate(());
#[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ExportOptions {
#[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)]
pub simplify_memories: bool,
#[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = "replace-with-bundle-of-uints")]
pub simplify_enums: std::option::Option<SimplifyEnumsKind>,
#[doc(hidden)]
#[clap(skip = ExportOptionsPrivate(()))]
/// `#[non_exhaustive]` except allowing struct update syntax
pub __private: ExportOptionsPrivate,
}
impl fmt::Debug for ExportOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug_fmt(f, |_| false)
}
}
impl ExportOptions {
fn debug_fmt(
&self,
f: &mut fmt::Formatter<'_>,
force_include_field: impl Fn(&str) -> bool,
) -> fmt::Result {
let Self {
simplify_memories,
simplify_enums,
__private: _,
} = *self;
f.write_str("ExportOptions {")?;
let mut sep = if f.alternate() { "\n " } else { " " };
let comma_sep = if f.alternate() { ",\n " } else { ", " };
let default = ExportOptions::default();
if simplify_memories != default.simplify_memories
|| force_include_field("simplify_memories")
{
write!(f, "{sep}simplify_memories: {:?}", simplify_memories)?;
sep = comma_sep;
}
if simplify_enums != default.simplify_enums || force_include_field("simplify_enums") {
write!(f, "{sep}simplify_enums: ")?;
macro_rules! debug_cases {
($($ident:ident $(($($args:tt)*))?,)*) => {
match simplify_enums {
// use more complex stringify to avoid the compiler inserting spaces
$($ident $(($($args)*))? => {
f.write_str(concat!(
stringify!($ident),
$("(",
$(stringify!($args),)*
")")?
))?;
})*
}
};
}
debug_cases! {
Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody),
Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
Some(SimplifyEnumsKind::ReplaceWithUInt),
None,
}
sep = comma_sep;
}
write!(
f,
"{sep}..ExportOptions::default(){}",
if f.alternate() { "\n}" } else { " }" }
)
}
}
impl Default for ExportOptions {
fn default() -> Self {
Self {
simplify_memories: true,
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
__private: ExportOptionsPrivate(()),
}
}
}
pub fn export<T: BundleType, B: FileBackendTrait>( pub fn export<T: BundleType, B: FileBackendTrait>(
file_backend: B, file_backend: B,
top_module: &Module<T>, top_module: &Module<T>,
options: ExportOptions,
) -> Result<B, B::Error> { ) -> Result<B, B::Error> {
let top_module = Intern::intern_sized(top_module.canonical()); let top_module = Intern::intern_sized(top_module.canonical());
WrappedFileBackend::with(file_backend, |file_backend| { WrappedFileBackend::with(file_backend, |file_backend| {
export_impl(file_backend, top_module, options) export_impl(file_backend, top_module)
}) })
} }
#[doc(hidden)] #[doc(hidden)]
#[track_caller] #[track_caller]
pub fn assert_export_firrtl_impl<T: BundleType>(top_module: &Module<T>, expected: TestBackend) { pub fn assert_export_firrtl_impl<T: BundleType>(top_module: &Module<T>, expected: TestBackend) {
let result = export( let result = export(TestBackend::default(), top_module).unwrap();
TestBackend {
files: BTreeMap::default(),
error_after: expected.error_after,
options: expected.options,
__private: expected.__private,
},
top_module,
expected.options,
)
.unwrap();
if result != expected { if result != expected {
panic!( panic!(
"assert_export_firrtl failed:\nyou can update the expected output by using:\n-------START-------\n{result:?}\n-------END-------" "assert_export_firrtl failed:\nyou can update the expected output by using:\n-------START-------\n{result:?}\n-------END-------"
@ -2783,69 +2476,21 @@ pub fn make_test_expected_files(v: &[(&str, &str)]) -> BTreeMap<String, String>
macro_rules! assert_export_firrtl { macro_rules! assert_export_firrtl {
{ {
$m:ident => $m:ident =>
$($field:ident: $value:expr,)*
@parsed_fields($($field_strings:expr,)*)
$($file_name:literal: $file_contents:literal,)* $($file_name:literal: $file_contents:literal,)*
$($field:ident: $value:expr,)*
} => { } => {
$crate::firrtl::assert_export_firrtl_impl( $crate::firrtl::assert_export_firrtl_impl(
&$m, &$m,
$crate::firrtl::TestBackend { $crate::firrtl::TestBackend {
$($field: $value,)*
files: $crate::firrtl::make_test_expected_files(&[ files: $crate::firrtl::make_test_expected_files(&[
$(($file_name, $file_contents),)* $(($file_name, $file_contents),)*
]), ]),
$($field: $value,)*
__private: $crate::firrtl::TestBackendPrivate { __private: $crate::firrtl::TestBackendPrivate {
module_var_name: stringify!($m), module_var_name: stringify!($m),
included_fields: &[$($field_strings,)*],
}, },
..<$crate::firrtl::TestBackend as $crate::__std::default::Default>::default() ..<$crate::firrtl::TestBackend as $crate::__std::default::Default>::default()
}, },
); );
}; };
{
$m:ident =>
$($parsed_fields:ident: $parsed_field_values:expr,)*
@parsed_fields($($field_strings:expr,)*)
options: ExportOptions {
$($export_option_fields:ident: $parsed_export_option_field_values:expr,)*
..$export_option_default:expr
},
$($rest:tt)*
} => {
$crate::assert_export_firrtl!(
$m =>
$($parsed_fields: $parsed_field_values,)*
options: ExportOptions {
$($export_option_fields: $parsed_export_option_field_values,)*
..$export_option_default
},
@parsed_fields($($field_strings,)* "options", $(concat!("options.", stringify!($export_option_fields)),)*)
$($rest)*
);
};
{
$m:ident =>
$($parsed_fields:ident: $parsed_field_values:expr,)*
@parsed_fields($($field_strings:expr,)*)
$field:ident: $field_value:expr,
$($rest:tt)*
} => {
$crate::assert_export_firrtl!(
$m =>
$($parsed_fields: $parsed_field_values,)*
$field: $field_value,
@parsed_fields($($field_strings,)* stringify!($field),)
$($rest)*
);
};
{
$m:ident =>
$($rest:tt)*
} => {
$crate::assert_export_firrtl!(
$m =>
@parsed_fields()
$($rest)*
);
};
} }

View file

@ -1,247 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
int::BoolOrIntType,
intern::{Intern, Interned, Memoize},
prelude::*,
};
use std::sync::OnceLock;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum FormalKind {
Assert,
Assume,
Cover,
}
impl FormalKind {
pub fn as_str(self) -> &'static str {
match self {
Self::Assert => "assert",
Self::Assume => "assume",
Self::Cover => "cover",
}
}
}
#[track_caller]
pub fn formal_stmt_with_enable_and_loc(
kind: FormalKind,
clk: Expr<Clock>,
pred: Expr<Bool>,
en: Expr<Bool>,
text: &str,
source_location: SourceLocation,
) {
crate::module::add_stmt_formal(crate::module::StmtFormal {
kind,
clk,
pred,
en: en & !formal_reset().cast_to_static::<Bool>(),
text: text.intern(),
source_location,
});
}
#[track_caller]
pub fn formal_stmt_with_enable(
kind: FormalKind,
clk: Expr<Clock>,
pred: Expr<Bool>,
en: Expr<Bool>,
text: &str,
) {
formal_stmt_with_enable_and_loc(kind, clk, pred, en, text, SourceLocation::caller());
}
#[track_caller]
pub fn formal_stmt_with_loc(
kind: FormalKind,
clk: Expr<Clock>,
pred: Expr<Bool>,
text: &str,
source_location: SourceLocation,
) {
formal_stmt_with_enable_and_loc(kind, clk, pred, true.to_expr(), text, source_location);
}
#[track_caller]
pub fn formal_stmt(kind: FormalKind, clk: Expr<Clock>, pred: Expr<Bool>, text: &str) {
formal_stmt_with_loc(kind, clk, pred, text, SourceLocation::caller());
}
macro_rules! make_formal {
($kind:ident, $formal_stmt_with_enable_and_loc:ident, $formal_stmt_with_enable:ident, $formal_stmt_with_loc:ident, $formal_stmt:ident) => {
#[track_caller]
pub fn $formal_stmt_with_enable_and_loc(
clk: Expr<Clock>,
pred: Expr<Bool>,
en: Expr<Bool>,
text: &str,
source_location: SourceLocation,
) {
formal_stmt_with_enable_and_loc(
FormalKind::$kind,
clk,
pred,
en,
text,
source_location,
);
}
#[track_caller]
pub fn $formal_stmt_with_enable(
clk: Expr<Clock>,
pred: Expr<Bool>,
en: Expr<Bool>,
text: &str,
) {
formal_stmt_with_enable(FormalKind::$kind, clk, pred, en, text);
}
#[track_caller]
pub fn $formal_stmt_with_loc(
clk: Expr<Clock>,
pred: Expr<Bool>,
text: &str,
source_location: SourceLocation,
) {
formal_stmt_with_loc(FormalKind::$kind, clk, pred, text, source_location);
}
#[track_caller]
pub fn $formal_stmt(clk: Expr<Clock>, pred: Expr<Bool>, text: &str) {
formal_stmt(FormalKind::$kind, clk, pred, text);
}
};
}
make_formal!(
Assert,
hdl_assert_with_enable_and_loc,
hdl_assert_with_enable,
hdl_assert_with_loc,
hdl_assert
);
make_formal!(
Assume,
hdl_assume_with_enable_and_loc,
hdl_assume_with_enable,
hdl_assume_with_loc,
hdl_assume
);
make_formal!(
Cover,
hdl_cover_with_enable_and_loc,
hdl_cover_with_enable,
hdl_cover_with_loc,
hdl_cover
);
pub trait MakeFormalExpr: Type {}
impl<T: Type> MakeFormalExpr for T {}
#[hdl]
pub fn formal_global_clock() -> Expr<Clock> {
#[hdl_module(extern)]
fn formal_global_clock() {
#[hdl]
let clk: Clock = m.output();
m.annotate_module(BlackBoxInlineAnnotation {
path: "fayalite_formal_global_clock.v".intern(),
text: r"module __fayalite_formal_global_clock(output clk);
(* gclk *)
reg clk;
endmodule
"
.intern(),
});
m.verilog_name("__fayalite_formal_global_clock");
}
#[hdl]
let formal_global_clock = instance(formal_global_clock());
formal_global_clock.clk
}
#[hdl]
pub fn formal_reset() -> Expr<SyncReset> {
#[hdl_module(extern)]
fn formal_reset() {
#[hdl]
let rst: SyncReset = m.output();
m.annotate_module(BlackBoxInlineAnnotation {
path: "fayalite_formal_reset.v".intern(),
text: r"module __fayalite_formal_reset(output rst);
assign rst = $initstate;
endmodule
"
.intern(),
});
m.verilog_name("__fayalite_formal_reset");
}
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 {
($ident:ident, $verilog_attribute:literal) => {
#[hdl]
pub fn $ident<T: BoolOrIntType>(ty: T) -> Expr<T> {
#[hdl_module(extern)]
pub(super) fn $ident<T: BoolOrIntType>(ty: T) {
#[hdl]
let out: T = m.output(ty);
let width = ty.width();
let verilog_bitslice = if width == 1 {
String::new()
} else {
format!(" [{}:0]", width - 1)
};
m.annotate_module(BlackBoxInlineAnnotation {
path: Intern::intern_owned(format!(
"fayalite_{}_{width}.v",
stringify!($ident),
)),
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, "anyconst");
make_any_const_fn!(any_seq, "anyseq");
make_any_const_fn!(all_const, "allconst");
make_any_const_fn!(all_seq, "allseq");

View file

@ -18,7 +18,6 @@ use std::{
borrow::{BorrowMut, Cow}, borrow::{BorrowMut, Cow},
fmt, fmt,
marker::PhantomData, marker::PhantomData,
num::NonZero,
ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive}, ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive},
sync::Arc, sync::Arc,
}; };
@ -32,23 +31,8 @@ mod sealed {
pub const DYN_SIZE: usize = !0; pub const DYN_SIZE: usize = !0;
pub type DynSize = ConstUsize<DYN_SIZE>; pub type DynSize = ConstUsize<DYN_SIZE>;
pub trait KnownSize: pub trait KnownSize: GenericConstUsize + Size<SizeType = Self> {
GenericConstUsize + sealed::SizeTypeSealed + sealed::SizeSealed + Default
{
const SIZE: Self; const SIZE: Self;
type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]>
+ AsMut<[Expr<Element>]>
+ BorrowMut<[Expr<Element>]>
+ 'static
+ Send
+ Sync
+ Eq
+ Clone
+ std::hash::Hash
+ std::fmt::Debug
+ IntoIterator<Item = Expr<Element>>
+ TryFrom<Vec<Expr<Element>>>
+ Into<Vec<Expr<Element>>>;
} }
macro_rules! known_widths { macro_rules! known_widths {
@ -59,7 +43,6 @@ macro_rules! known_widths {
v v
}> { }> {
const SIZE: Self = Self; const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
} }
}; };
([2 $($rest:tt)*] $($bits:literal)+) => { ([2 $($rest:tt)*] $($bits:literal)+) => {
@ -71,7 +54,6 @@ macro_rules! known_widths {
known_widths!([$($rest)*] 1); known_widths!([$($rest)*] 1);
impl KnownSize for ConstUsize<{2 $(* $rest)*}> { impl KnownSize for ConstUsize<{2 $(* $rest)*}> {
const SIZE: Self = Self; const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
} }
}; };
} }
@ -141,24 +123,30 @@ impl<const VALUE: usize> sealed::SizeSealed for ConstUsize<VALUE> {}
impl<const VALUE: usize> sealed::SizeTypeSealed for ConstUsize<VALUE> {} impl<const VALUE: usize> sealed::SizeTypeSealed for ConstUsize<VALUE> {}
impl<T: KnownSize> SizeType for T { impl<const VALUE: usize> SizeType for ConstUsize<VALUE>
type Size = T; where
ConstUsize<VALUE>: KnownSize,
{
type Size = ConstUsize<VALUE>;
} }
impl<T: KnownSize> Size for T { impl<const VALUE: usize> Size for ConstUsize<VALUE>
type ArrayMatch<Element: Type> = <T as KnownSize>::ArrayMatch<Element>; where
ConstUsize<VALUE>: KnownSize,
{
type ArrayMatch<Element: Type> = [Expr<Element>; VALUE];
const KNOWN_VALUE: Option<usize> = Some(T::VALUE); const KNOWN_VALUE: Option<usize> = Some(VALUE);
type SizeType = T; type SizeType = ConstUsize<VALUE>;
fn as_usize(_size_type: Self::SizeType) -> usize { fn as_usize(_size_type: Self::SizeType) -> usize {
T::VALUE VALUE
} }
fn try_from_usize(v: usize) -> Option<Self::SizeType> { fn try_from_usize(v: usize) -> Option<Self::SizeType> {
if v == T::VALUE { if v == VALUE {
Some(T::SIZE) Some(Self::SizeType::default())
} else { } else {
None None
} }
@ -202,17 +190,17 @@ macro_rules! impl_int {
bit_width: self.width(), bit_width: self.width(),
} }
} }
pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec { pub fn bits_from_bigint_wrapping(self, v: BigInt) -> BitVec {
BoolOrIntType::bits_from_bigint_wrapping(self, v) BoolOrIntType::bits_from_bigint_wrapping(self, v)
} }
pub fn from_bigint_wrapping(self, v: &BigInt) -> $value<Width> { pub fn from_bigint_wrapping(self, v: BigInt) -> $value<Width> {
$value { $value {
bits: Arc::new(self.bits_from_bigint_wrapping(v)), bits: Arc::new(self.bits_from_bigint_wrapping(v)),
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
pub fn from_int_wrapping(self, v: impl Into<BigInt>) -> $value<Width> { pub fn from_int_wrapping(self, v: impl Into<BigInt>) -> $value<Width> {
self.from_bigint_wrapping(&v.into()) self.from_bigint_wrapping(v.into())
} }
pub fn zero(self) -> $value<Width> { pub fn zero(self) -> $value<Width> {
self.from_int_wrapping(0u8) self.from_int_wrapping(0u8)
@ -227,29 +215,12 @@ macro_rules! impl_int {
impl<Width: Size> BoolOrIntType for $name<Width> { impl<Width: Size> BoolOrIntType for $name<Width> {
type Width = Width; type Width = Width;
type Signed = ConstBool<$SIGNED>; type Signed = ConstBool<$SIGNED>;
type Value = $value<Width>;
fn width(self) -> usize { fn width(self) -> usize {
$name::width(self) $name::width(self)
} }
fn new(width: Width::SizeType) -> Self { fn new(width: Width::SizeType) -> Self {
$name { width } $name { width }
} }
fn value_from_bigint_wrapping(self, v: &BigInt) -> Self::Value {
$value::<Width>::from_bigint_wrapping(self, v)
}
fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value {
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
struct MemoizeBitsToValue;
impl Memoize for MemoizeBitsToValue {
type Input = BitSlice;
type InputOwned = BitVec;
type Output = Arc<BitVec>;
fn inner(self, input: &Self::Input) -> Self::Output {
Arc::new(input.to_bitvec())
}
}
$value::new(MemoizeBitsToValue.get_cow(bits))
}
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> { fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
#[derive(Copy, Clone, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
struct MemoizeBitsToExpr; struct MemoizeBitsToExpr;
@ -280,7 +251,9 @@ macro_rules! impl_int {
impl<Width: KnownSize> $name<Width> { impl<Width: KnownSize> $name<Width> {
pub fn new_static() -> Self { pub fn new_static() -> Self {
Self { width: Width::SIZE } Self {
width: Width::SizeType::default(),
}
} }
} }
@ -327,7 +300,7 @@ macro_rules! impl_int {
type Output = $name<Width::Size>; type Output = $name<Width::Size>;
fn index(&self, width: Width) -> &Self::Output { fn index(&self, width: Width) -> &Self::Output {
Interned::into_inner(Intern::intern_sized($name::new(width))) Interned::<_>::into_inner(Intern::intern_sized($name::new(width)))
} }
} }
@ -351,24 +324,6 @@ macro_rules! impl_int {
} }
} }
impl<Width: Size> PartialOrd for $value<Width> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<Width: Size> Ord for $value<Width> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.to_bigint().cmp(&other.to_bigint())
}
}
impl<Width: Size> From<$value<Width>> for BigInt {
fn from(v: $value<Width>) -> BigInt {
v.to_bigint()
}
}
impl<Width: Size> $value<Width> { impl<Width: Size> $value<Width> {
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
if let Some(retval) = Width::KNOWN_VALUE { if let Some(retval) = Width::KNOWN_VALUE {
@ -378,7 +333,7 @@ macro_rules! impl_int {
self.bits.len() self.bits.len()
} }
} }
pub fn from_bigint_wrapping(ty: $name<Width>, v: &BigInt) -> $value<Width> { pub fn from_bigint_wrapping(ty: $name<Width>, v: BigInt) -> $value<Width> {
ty.from_bigint_wrapping(v) ty.from_bigint_wrapping(v)
} }
pub fn to_bigint(&self) -> BigInt { pub fn to_bigint(&self) -> BigInt {
@ -488,10 +443,7 @@ impl SInt {
v.not().bits().checked_add(1).expect("too big") v.not().bits().checked_add(1).expect("too big")
} }
Sign::NoSign => 0, Sign::NoSign => 0,
Sign::Plus => { Sign::Plus => v.bits(),
// account for sign bit
v.bits().checked_add(1).expect("too big")
}
} }
.try_into() .try_into()
.expect("too big"), .expect("too big"),
@ -516,24 +468,7 @@ impl SInt {
} }
macro_rules! impl_prim_int { macro_rules! impl_prim_int {
( ($prim_int:ident, $ty:ty) => {
$(#[$meta:meta])*
$prim_int:ident, $ty:ty
) => {
impl From<$prim_int> for <$ty as BoolOrIntType>::Value {
fn from(v: $prim_int) -> Self {
<$ty>::le_bytes_to_value_wrapping(
&v.to_le_bytes(),
<$ty as BoolOrIntType>::Width::VALUE,
)
}
}
impl From<NonZero<$prim_int>> for <$ty as BoolOrIntType>::Value {
fn from(v: NonZero<$prim_int>) -> Self {
v.get().into()
}
}
$(#[$meta])*
impl ToExpr for $prim_int { impl ToExpr for $prim_int {
type Type = $ty; type Type = $ty;
@ -544,14 +479,6 @@ macro_rules! impl_prim_int {
) )
} }
} }
$(#[$meta])*
impl ToExpr for NonZero<$prim_int> {
type Type = $ty;
fn to_expr(&self) -> Expr<Self::Type> {
self.get().to_expr()
}
}
}; };
} }
@ -566,35 +493,16 @@ impl_prim_int!(i32, SInt<32>);
impl_prim_int!(i64, SInt<64>); impl_prim_int!(i64, SInt<64>);
impl_prim_int!(i128, SInt<128>); impl_prim_int!(i128, SInt<128>);
impl_prim_int!(
/// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
usize, UInt<64>
);
impl_prim_int!(
/// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
isize, SInt<64>
);
pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
type Width: Size; type Width: Size;
type Signed: GenericConstBool; type Signed: GenericConstBool;
type Value: Clone
+ Ord
+ std::hash::Hash
+ fmt::Debug
+ Send
+ Sync
+ 'static
+ ToExpr<Type = Self>
+ Into<BigInt>;
fn width(self) -> usize; fn width(self) -> usize;
fn new(width: <Self::Width as Size>::SizeType) -> Self; fn new(width: <Self::Width as Size>::SizeType) -> Self;
fn new_static() -> Self fn new_static() -> Self
where where
Self::Width: KnownSize + Size<SizeType = Self::Width>, Self::Width: KnownSize,
{ {
Self::new(Self::Width::default()) Self::new(<Self::Width as Size>::SizeType::default())
} }
fn as_same_width_sint(self) -> SIntType<Self::Width> { fn as_same_width_sint(self) -> SIntType<Self::Width> {
SIntType::new(Self::Width::from_usize(self.width())) SIntType::new(Self::Width::from_usize(self.width()))
@ -602,24 +510,17 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
fn as_same_width_uint(self) -> UIntType<Self::Width> { fn as_same_width_uint(self) -> UIntType<Self::Width> {
UIntType::new(Self::Width::from_usize(self.width())) UIntType::new(Self::Width::from_usize(self.width()))
} }
fn value_from_int_wrapping(self, v: impl Into<BigInt>) -> Self::Value { fn bits_from_bigint_wrapping(self, v: BigInt) -> BitVec {
self.value_from_bigint_wrapping(&v.into()) let width = self.width();
}
fn value_from_bigint_wrapping(self, v: &BigInt) -> Self::Value;
fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec {
let mut bits = BitVec::repeat(false, self.width());
Self::copy_bits_from_bigint_wrapping(v, &mut bits);
bits
}
fn copy_bits_from_bigint_wrapping(v: &BigInt, bits: &mut BitSlice) {
let width = bits.len();
let mut bytes = v.to_signed_bytes_le(); let mut bytes = v.to_signed_bytes_le();
bytes.resize( bytes.resize(
width.div_ceil(u8::BITS as usize), width.div_ceil(u8::BITS as usize),
if v.is_negative() { 0xFF } else { 0 }, if v.is_negative() { 0xFF } else { 0 },
); );
let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width]; let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width];
bits.clone_from_bitslice(bitslice); let mut bits = BitVec::new();
bits.extend_from_bitslice(bitslice);
bits
} }
fn bits_to_bigint(bits: &BitSlice) -> BigInt { fn bits_to_bigint(bits: &BitSlice) -> BigInt {
let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) {
@ -631,10 +532,9 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
BitSlice::<u8, Lsb0>::from_slice_mut(&mut bytes)[..bits.len()].clone_from_bitslice(bits); BitSlice::<u8, Lsb0>::from_slice_mut(&mut bytes)[..bits.len()].clone_from_bitslice(bits);
BigInt::from_signed_bytes_le(&bytes) BigInt::from_signed_bytes_le(&bytes)
} }
fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value;
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self>; fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self>;
fn le_bytes_to_bits_wrapping(bytes: &[u8], bit_width: usize) -> BitVec { fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr<Self> {
let bitslice = BitSlice::<u8, Lsb0>::from_slice(bytes); let bitslice = BitSlice::<u8, Lsb0>::from_slice(&bytes);
let bitslice = &bitslice[..bit_width.min(bitslice.len())]; let bitslice = &bitslice[..bit_width.min(bitslice.len())];
let mut bits = BitVec::new(); let mut bits = BitVec::new();
bits.extend_from_bitslice(bitslice); bits.extend_from_bitslice(bitslice);
@ -642,17 +542,7 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
bit_width, bit_width,
Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false), Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false),
); );
bits Self::bits_to_expr(Cow::Owned(bits))
}
fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr<Self> {
Self::bits_to_expr(Cow::Owned(Self::le_bytes_to_bits_wrapping(
bytes, bit_width,
)))
}
fn le_bytes_to_value_wrapping(bytes: &[u8], bit_width: usize) -> Self::Value {
Self::bits_to_value(Cow::Owned(Self::le_bytes_to_bits_wrapping(
bytes, bit_width,
)))
} }
} }
@ -704,7 +594,6 @@ impl sealed::BoolOrIntTypeSealed for Bool {}
impl BoolOrIntType for Bool { impl BoolOrIntType for Bool {
type Width = ConstUsize<1>; type Width = ConstUsize<1>;
type Signed = ConstBool<false>; type Signed = ConstBool<false>;
type Value = bool;
fn width(self) -> usize { fn width(self) -> usize {
1 1
@ -715,19 +604,10 @@ impl BoolOrIntType for Bool {
Bool Bool
} }
fn value_from_bigint_wrapping(self, v: &BigInt) -> Self::Value {
v.bit(0)
}
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> { fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
assert_eq!(bits.len(), 1); assert_eq!(bits.len(), 1);
bits[0].to_expr() bits[0].to_expr()
} }
fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value {
assert_eq!(bits.len(), 1);
bits[0]
}
} }
impl Bool { impl Bool {
@ -773,36 +653,17 @@ impl StaticType for Bool {
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
} }
pub trait IntCmp<Rhs> {
fn cmp_eq(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_ne(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_lt(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_le(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_gt(self, rhs: Rhs) -> Expr<Bool>;
fn cmp_ge(self, rhs: Rhs) -> Expr<Bool>;
}
impl ToLiteralBits for bool { impl ToLiteralBits for bool {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> { fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
Ok(interned_bit(*self)) Ok(interned_bit(*self))
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_uint_for_value() {
assert_eq!(UInt::for_value(0u8).width, 0);
assert_eq!(UInt::for_value(1u8).width, 1);
assert_eq!(UInt::for_value(2u8).width, 2);
assert_eq!(UInt::for_value(3u8).width, 2);
assert_eq!(UInt::for_value(4u8).width, 3);
}
#[test]
fn test_sint_for_value() {
assert_eq!(SInt::for_value(-5).width, 4);
assert_eq!(SInt::for_value(-4).width, 3);
assert_eq!(SInt::for_value(-3).width, 3);
assert_eq!(SInt::for_value(-2).width, 2);
assert_eq!(SInt::for_value(-1).width, 1);
assert_eq!(SInt::for_value(0).width, 0);
assert_eq!(SInt::for_value(1).width, 2);
assert_eq!(SInt::for_value(2).width, 3);
assert_eq!(SInt::for_value(3).width, 3);
assert_eq!(SInt::for_value(4).width, 4);
}
}

File diff suppressed because it is too large Load diff

View file

@ -11,59 +11,6 @@ extern crate self as fayalite;
#[doc(hidden)] #[doc(hidden)]
pub use std as __std; pub use std as __std;
#[doc(hidden)]
#[macro_export]
macro_rules! __cfg_expansion_helper {
(
[
$($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)*
]
[
$cfg:ident($($expr:tt)*),
$($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)*
]
// pass as tt so we get right span for attribute
$after_evaluation_attr:tt $after_evaluation_body:tt
) => {
#[$cfg($($expr)*)]
$crate::__cfg_expansion_helper! {
[
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
$cfg($($expr)*) = true,
]
[
$($unevaluated_cfgs($($unevaluated_exprs)*),)*
]
$after_evaluation_attr $after_evaluation_body
}
#[$cfg(not($($expr)*))]
$crate::__cfg_expansion_helper! {
[
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
$cfg($($expr)*) = false,
]
[
$($unevaluated_cfgs($($unevaluated_exprs)*),)*
]
$after_evaluation_attr $after_evaluation_body
}
};
(
[
$($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)*
]
[]
// don't use #[...] so we get right span for `#` and `[]` of attribute
{$($after_evaluation_attr:tt)*} {$($after_evaluation_body:tt)*}
) => {
$($after_evaluation_attr)*
#[__evaluated_cfgs([
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
])]
$($after_evaluation_body)*
};
}
#[doc(inline)] #[doc(inline)]
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
/// a [`Module`][`::fayalite::module::Module`] when called. /// a [`Module`][`::fayalite::module::Module`] when called.
@ -83,6 +30,7 @@ pub struct __;
#[cfg(feature = "unstable-doc")] #[cfg(feature = "unstable-doc")]
pub mod _docs; pub mod _docs;
// FIXME: finish
pub mod annotations; pub mod annotations;
pub mod array; pub mod array;
pub mod bundle; pub mod bundle;
@ -91,17 +39,15 @@ pub mod clock;
pub mod enum_; pub mod enum_;
pub mod expr; pub mod expr;
pub mod firrtl; pub mod firrtl;
pub mod formal;
pub mod int; pub mod int;
pub mod intern; pub mod intern;
pub mod memory; pub mod memory;
pub mod module; pub mod module;
pub mod prelude;
pub mod reg; pub mod reg;
pub mod reset; pub mod reset;
pub mod sim;
pub mod source_location; pub mod source_location;
pub mod testing;
pub mod ty; pub mod ty;
pub mod util; pub mod util;
//pub mod valueless;
pub mod prelude;
pub mod wire; pub mod wire;

View file

@ -7,7 +7,7 @@ use crate::{
array::{Array, ArrayType}, array::{Array, ArrayType},
bundle::{Bundle, BundleType}, bundle::{Bundle, BundleType},
clock::Clock, clock::Clock,
expr::{ops::BundleLiteral, repeat, Expr, Flow, ToExpr, ToLiteralBits}, expr::{Expr, Flow, ToExpr, ToLiteralBits},
hdl, hdl,
int::{Bool, DynSize, Size, UInt, UIntType}, int::{Bool, DynSize, Size, UInt, UIntType},
intern::{Intern, Interned}, intern::{Intern, Interned},
@ -22,7 +22,7 @@ use std::{
fmt, fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
marker::PhantomData, marker::PhantomData,
num::NonZeroUsize, num::NonZeroU32,
rc::Rc, rc::Rc,
}; };
@ -478,7 +478,7 @@ struct MemImpl<Element: Type, Len: Size, P> {
initial_value: Option<Interned<BitSlice>>, initial_value: Option<Interned<BitSlice>>,
ports: P, ports: P,
read_latency: usize, read_latency: usize,
write_latency: NonZeroUsize, write_latency: NonZeroU32,
read_under_write: ReadUnderWrite, read_under_write: ReadUnderWrite,
port_annotations: Interned<[TargetedAnnotation]>, port_annotations: Interned<[TargetedAnnotation]>,
mem_annotations: Interned<[Annotation]>, mem_annotations: Interned<[Annotation]>,
@ -519,12 +519,7 @@ impl<Element: Type, Len: Size> fmt::Debug for Mem<Element, Len> {
f.debug_struct("Mem") f.debug_struct("Mem")
.field("name", scoped_name) .field("name", scoped_name)
.field("array_type", array_type) .field("array_type", array_type)
.field( .field("initial_value", initial_value)
"initial_value",
&initial_value.as_ref().map(|initial_value| {
DebugMemoryData::from_bit_slice(*array_type, initial_value)
}),
)
.field("read_latency", read_latency) .field("read_latency", read_latency)
.field("write_latency", write_latency) .field("write_latency", write_latency)
.field("read_under_write", read_under_write) .field("read_under_write", read_under_write)
@ -567,7 +562,7 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
initial_value: Option<Interned<BitSlice>>, initial_value: Option<Interned<BitSlice>>,
ports: Interned<[MemPort<DynPortType>]>, ports: Interned<[MemPort<DynPortType>]>,
read_latency: usize, read_latency: usize,
write_latency: NonZeroUsize, write_latency: NonZeroU32,
read_under_write: ReadUnderWrite, read_under_write: ReadUnderWrite,
port_annotations: Interned<[TargetedAnnotation]>, port_annotations: Interned<[TargetedAnnotation]>,
mem_annotations: Interned<[Annotation]>, mem_annotations: Interned<[Annotation]>,
@ -639,7 +634,7 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
self.0.source_location self.0.source_location
} }
pub fn array_type(self) -> ArrayType<Element, Len> { pub fn array_type(self) -> ArrayType<Element, Len> {
self.0.array_type self.0.array_type.clone()
} }
pub fn initial_value(self) -> Option<Interned<BitSlice>> { pub fn initial_value(self) -> Option<Interned<BitSlice>> {
self.0.initial_value self.0.initial_value
@ -650,7 +645,7 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
pub fn read_latency(self) -> usize { pub fn read_latency(self) -> usize {
self.0.read_latency self.0.read_latency
} }
pub fn write_latency(self) -> NonZeroUsize { pub fn write_latency(self) -> NonZeroU32 {
self.0.write_latency self.0.write_latency
} }
pub fn read_under_write(self) -> ReadUnderWrite { pub fn read_under_write(self) -> ReadUnderWrite {
@ -712,7 +707,7 @@ pub(crate) struct MemBuilderTarget {
pub(crate) initial_value: Option<Interned<BitSlice>>, pub(crate) initial_value: Option<Interned<BitSlice>>,
pub(crate) ports: Vec<MemPort<DynPortType>>, pub(crate) ports: Vec<MemPort<DynPortType>>,
pub(crate) read_latency: usize, pub(crate) read_latency: usize,
pub(crate) write_latency: NonZeroUsize, pub(crate) write_latency: NonZeroU32,
pub(crate) read_under_write: ReadUnderWrite, pub(crate) read_under_write: ReadUnderWrite,
pub(crate) port_annotations: Vec<TargetedAnnotation>, pub(crate) port_annotations: Vec<TargetedAnnotation>,
pub(crate) mem_annotations: Vec<Annotation>, pub(crate) mem_annotations: Vec<Annotation>,
@ -872,7 +867,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
initial_value: None, initial_value: None,
ports: vec![], ports: vec![],
read_latency: 0, read_latency: 0,
write_latency: NonZeroUsize::new(1).unwrap(), write_latency: NonZeroU32::new(1).unwrap(),
read_under_write: ReadUnderWrite::Old, read_under_write: ReadUnderWrite::Old,
port_annotations: vec![], port_annotations: vec![],
mem_annotations: vec![], mem_annotations: vec![],
@ -992,7 +987,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
#[allow(clippy::result_unit_err)] #[allow(clippy::result_unit_err)]
pub fn get_array_type(&self) -> Result<ArrayType<Element, Len>, ()> { pub fn get_array_type(&self) -> Result<ArrayType<Element, Len>, ()> {
Ok(ArrayType::new( Ok(ArrayType::new(
self.mem_element_type, self.mem_element_type.clone(),
Len::from_usize(self.get_depth()?), Len::from_usize(self.get_depth()?),
)) ))
} }
@ -1035,10 +1030,10 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
pub fn read_latency(&mut self, read_latency: usize) { pub fn read_latency(&mut self, read_latency: usize) {
self.target.borrow_mut().read_latency = read_latency; self.target.borrow_mut().read_latency = read_latency;
} }
pub fn get_write_latency(&self) -> NonZeroUsize { pub fn get_write_latency(&self) -> NonZeroU32 {
self.target.borrow().write_latency self.target.borrow().write_latency
} }
pub fn write_latency(&mut self, write_latency: NonZeroUsize) { pub fn write_latency(&mut self, write_latency: NonZeroU32) {
self.target.borrow_mut().write_latency = write_latency; self.target.borrow_mut().write_latency = write_latency;
} }
pub fn get_read_under_write(&self) -> ReadUnderWrite { pub fn get_read_under_write(&self) -> ReadUnderWrite {
@ -1055,90 +1050,3 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
.extend(annotations.into_annotations()); .extend(annotations.into_annotations());
} }
} }
pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
let canonical_ty = ty.canonical();
match canonical_ty {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::Enum(_) => Expr::from_canonical(Expr::canonical(value)),
CanonicalType::Array(array) => Expr::from_canonical(Expr::canonical(repeat(
splat_mask(array.element(), value),
array.len(),
))),
CanonicalType::Bundle(bundle) => Expr::from_canonical(Expr::canonical(
BundleLiteral::new(
bundle.mask_type(),
bundle
.fields()
.iter()
.map(|field| splat_mask(field.ty, value))
.collect(),
)
.to_expr(),
)),
}
}
pub trait DebugMemoryDataGetElement {
fn get_element(&self, element_index: usize, array_type: Array) -> &BitSlice;
}
impl<'a, F: ?Sized + Fn(usize, Array) -> &'a BitSlice> DebugMemoryDataGetElement for &'a F {
fn get_element(&self, element_index: usize, array_type: Array) -> &BitSlice {
self(element_index, array_type)
}
}
#[derive(Clone)]
pub struct DebugMemoryData<GetElement: DebugMemoryDataGetElement> {
pub array_type: Array,
pub get_element: GetElement,
}
impl DebugMemoryDataGetElement for &'_ BitSlice {
fn get_element(&self, element_index: usize, array_type: Array) -> &BitSlice {
assert!(element_index < array_type.len());
let stride = array_type.element().bit_width();
let start = element_index
.checked_mul(stride)
.expect("memory is too big");
let end = start.checked_add(stride).expect("memory is too big");
&self[start..end]
}
}
impl<'a> DebugMemoryData<&'a BitSlice> {
pub fn from_bit_slice<T: Type, Depth: Size>(
array_type: ArrayType<T, Depth>,
bit_slice: &'a BitSlice,
) -> Self {
let array_type = array_type.as_dyn_array();
assert_eq!(bit_slice.len(), array_type.type_properties().bit_width);
Self {
array_type,
get_element: bit_slice,
}
}
}
impl<GetElement: DebugMemoryDataGetElement> fmt::Debug for DebugMemoryData<GetElement> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.array_type.len() == 0 {
return f.write_str("[]");
}
writeln!(f, "[\n // len = {:#x}", self.array_type.len())?;
for element_index in 0..self.array_type.len() {
let element = crate::util::BitSliceWriteWithBase(
self.get_element.get_element(element_index, self.array_type),
);
writeln!(f, " [{element_index:#x}]: {element:#x},")?;
}
f.write_str("]")
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
// 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 simplify_enums; pub mod simplify_enums;
pub mod simplify_memories; pub mod simplify_memories;
pub mod visit; pub mod visit;

File diff suppressed because it is too large Load diff

View file

@ -2,19 +2,19 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
array::{Array, ArrayType}, array::{Array, ArrayType},
bundle::{Bundle, BundleField, BundleType}, bundle::{Bundle, BundleType},
enum_::{Enum, EnumType, EnumVariant}, enum_::{Enum, EnumType, EnumVariant},
expr::{ expr::{
ops::{self, EnumLiteral}, ops::{self, EnumLiteral},
CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr,
}, },
hdl, hdl,
int::UInt, int::{DynSize, IntCmp, Size, UInt, UIntType},
intern::{Intern, Interned, Memoize}, intern::{Intern, Interned},
memory::{DynPortType, Mem, MemPort}, memory::{DynPortType, Mem, MemPort},
module::{ module::{
transform::visit::{Fold, Folder}, transform::visit::{Fold, Folder},
Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
@ -41,70 +41,25 @@ impl fmt::Display for SimplifyEnumsError {
impl std::error::Error for SimplifyEnumsError {} impl std::error::Error for SimplifyEnumsError {}
impl From<SimplifyEnumsError> for std::io::Error {
fn from(value: SimplifyEnumsError) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, value)
}
}
fn contains_any_enum_types(ty: CanonicalType) -> bool {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct TheMemoize;
impl Memoize for TheMemoize {
type Input = CanonicalType;
type InputOwned = CanonicalType;
type Output = bool;
fn inner(self, ty: &Self::Input) -> Self::Output {
match *ty {
CanonicalType::Array(array_type) => contains_any_enum_types(array_type.element()),
CanonicalType::Enum(_) => true,
CanonicalType::Bundle(bundle) => bundle
.fields()
.iter()
.any(|field| contains_any_enum_types(field.ty)),
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => false,
}
}
}
TheMemoize.get_owned(ty)
}
#[hdl] #[hdl]
struct TagAndBody<Tag, Body> { struct TagAndBody<T, BodyWidth: Size> {
tag: Tag, tag: T,
body: Body, body: UIntType<BodyWidth>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum EnumTypeState { enum EnumTypeState {
TagEnumAndBody(TagAndBody<Enum, UInt>), TagEnumAndBody(TagAndBody<Enum, DynSize>),
TagUIntAndBody(TagAndBody<UInt, UInt>), TagUIntAndBody(TagAndBody<UInt, DynSize>),
UInt(UInt), UInt(UInt),
Unchanged, Unchanged,
} }
struct ModuleState {
module_name: NameId,
}
impl ModuleState {
fn gen_name(&mut self, name: &str) -> ScopedNameId {
ScopedNameId(self.module_name, NameId(name.intern(), Id::new()))
}
}
struct State { struct State {
enum_types: HashMap<Enum, EnumTypeState>, enum_types: HashMap<Enum, EnumTypeState>,
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>, name_id_gen: NameIdGen,
} }
impl State { impl State {
@ -152,369 +107,6 @@ impl State {
self.enum_types.insert(enum_type, retval.clone()); self.enum_types.insert(enum_type, retval.clone());
Ok(retval) Ok(retval)
} }
#[hdl]
fn handle_enum_literal(
&mut self,
unfolded_enum_type: Enum,
variant_index: usize,
folded_variant_value: Option<Expr<CanonicalType>>,
) -> Result<Expr<CanonicalType>, SimplifyEnumsError> {
Ok(
match self.get_or_make_enum_type_state(unfolded_enum_type)? {
EnumTypeState::TagEnumAndBody(TagAndBody { tag, body }) => Expr::canonical(
#[hdl]
TagAndBody {
tag: EnumLiteral::new_by_index(tag, variant_index, None),
body: match folded_variant_value {
Some(variant_value) => variant_value.cast_to_bits().cast_to(body),
None => body.zero().to_expr(),
},
},
),
EnumTypeState::TagUIntAndBody(TagAndBody { tag, body }) => Expr::canonical(
#[hdl]
TagAndBody {
tag: tag.from_int_wrapping(variant_index),
body: match folded_variant_value {
Some(folded_variant_value) => {
folded_variant_value.cast_to_bits().cast_to(body)
}
None => body.zero().to_expr(),
},
},
),
EnumTypeState::UInt(_) => {
let tag = UInt[unfolded_enum_type.discriminant_bit_width()];
let body = UInt[unfolded_enum_type.type_properties().bit_width - tag.width()];
Expr::canonical(
(#[hdl]
TagAndBody {
tag: tag.from_int_wrapping(variant_index),
body: match folded_variant_value {
Some(folded_variant_value) => {
folded_variant_value.cast_to_bits().cast_to(body)
}
None => body.zero().to_expr(),
},
})
.cast_to_bits(),
)
}
EnumTypeState::Unchanged => Expr::canonical(
ops::EnumLiteral::new_by_index(
unfolded_enum_type,
variant_index,
folded_variant_value,
)
.to_expr(),
),
},
)
}
fn handle_variant_access(
&mut self,
unfolded_enum_type: Enum,
folded_base_expr: Expr<CanonicalType>,
variant_index: usize,
) -> Result<Expr<CanonicalType>, SimplifyEnumsError> {
let unfolded_variant_type = unfolded_enum_type.variants()[variant_index].ty;
Ok(
match self.get_or_make_enum_type_state(unfolded_enum_type)? {
EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => {
match unfolded_variant_type {
Some(variant_type) => Expr::canonical(
Expr::<TagAndBody<CanonicalType, UInt>>::from_canonical(
folded_base_expr,
)
.body[..variant_type.bit_width()]
.cast_bits_to(variant_type.fold(self)?),
),
None => Expr::canonical(().to_expr()),
}
}
EnumTypeState::UInt(_) => match unfolded_variant_type {
Some(variant_type) => {
let base_int = Expr::<UInt>::from_canonical(folded_base_expr);
let variant_type_bit_width = variant_type.bit_width();
Expr::canonical(
base_int[unfolded_enum_type.discriminant_bit_width()..]
[..variant_type_bit_width]
.cast_bits_to(variant_type.fold(self)?),
)
}
None => Expr::canonical(().to_expr()),
},
EnumTypeState::Unchanged => match unfolded_variant_type {
Some(_) => ops::VariantAccess::new_by_index(
Expr::from_canonical(folded_base_expr),
variant_index,
)
.to_expr(),
None => Expr::canonical(().to_expr()),
},
},
)
}
fn handle_match(
&mut self,
unfolded_enum_type: Enum,
folded_expr: Expr<CanonicalType>,
source_location: SourceLocation,
folded_blocks: &[Block],
) -> Result<Stmt, SimplifyEnumsError> {
match self.get_or_make_enum_type_state(unfolded_enum_type)? {
EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch {
expr: Expr::<TagAndBody<Enum, UInt>>::from_canonical(folded_expr).tag,
source_location,
blocks: folded_blocks.intern(),
}
.into()),
EnumTypeState::TagUIntAndBody(_) => {
let int_tag_expr = Expr::<TagAndBody<UInt, UInt>>::from_canonical(folded_expr).tag;
Ok(match_int_tag(int_tag_expr, source_location, folded_blocks).into())
}
EnumTypeState::UInt(_) => {
let int_tag_expr = Expr::<UInt>::from_canonical(folded_expr)
[..unfolded_enum_type.discriminant_bit_width()];
Ok(match_int_tag(int_tag_expr, source_location, folded_blocks).into())
}
EnumTypeState::Unchanged => Ok(StmtMatch {
expr: Expr::from_canonical(folded_expr),
source_location,
blocks: folded_blocks.intern(),
}
.into()),
}
}
fn handle_stmt_connect_array(
&mut self,
unfolded_lhs_ty: Array,
unfolded_rhs_ty: Array,
folded_lhs: Expr<Array>,
folded_rhs: Expr<Array>,
source_location: SourceLocation,
output_stmts: &mut Vec<Stmt>,
) -> Result<(), SimplifyEnumsError> {
assert_eq!(unfolded_lhs_ty.len(), unfolded_rhs_ty.len());
let unfolded_lhs_element_ty = unfolded_lhs_ty.element();
let unfolded_rhs_element_ty = unfolded_rhs_ty.element();
for array_index in 0..unfolded_lhs_ty.len() {
self.handle_stmt_connect(
unfolded_lhs_element_ty,
unfolded_rhs_element_ty,
folded_lhs[array_index],
folded_rhs[array_index],
source_location,
output_stmts,
)?;
}
Ok(())
}
fn handle_stmt_connect_bundle(
&mut self,
unfolded_lhs_ty: Bundle,
unfolded_rhs_ty: Bundle,
folded_lhs: Expr<Bundle>,
folded_rhs: Expr<Bundle>,
source_location: SourceLocation,
output_stmts: &mut Vec<Stmt>,
) -> Result<(), SimplifyEnumsError> {
let unfolded_lhs_fields = unfolded_lhs_ty.fields();
let unfolded_rhs_fields = unfolded_rhs_ty.fields();
assert_eq!(unfolded_lhs_fields.len(), unfolded_rhs_fields.len());
for (
field_index,
(
&BundleField {
name,
flipped,
ty: unfolded_lhs_field_ty,
},
unfolded_rhs_field,
),
) in unfolded_lhs_fields
.iter()
.zip(&unfolded_rhs_fields)
.enumerate()
{
assert_eq!(name, unfolded_rhs_field.name);
assert_eq!(flipped, unfolded_rhs_field.flipped);
let folded_lhs_field =
ops::FieldAccess::new_by_index(folded_lhs, field_index).to_expr();
let folded_rhs_field =
ops::FieldAccess::new_by_index(folded_rhs, field_index).to_expr();
if flipped {
// swap lhs/rhs
self.handle_stmt_connect(
unfolded_rhs_field.ty,
unfolded_lhs_field_ty,
folded_rhs_field,
folded_lhs_field,
source_location,
output_stmts,
)?;
} else {
self.handle_stmt_connect(
unfolded_lhs_field_ty,
unfolded_rhs_field.ty,
folded_lhs_field,
folded_rhs_field,
source_location,
output_stmts,
)?;
}
}
Ok(())
}
fn handle_stmt_connect_enum(
&mut self,
unfolded_lhs_ty: Enum,
unfolded_rhs_ty: Enum,
folded_lhs: Expr<CanonicalType>,
folded_rhs: Expr<CanonicalType>,
source_location: SourceLocation,
output_stmts: &mut Vec<Stmt>,
) -> Result<(), SimplifyEnumsError> {
let unfolded_lhs_variants = unfolded_lhs_ty.variants();
let unfolded_rhs_variants = unfolded_rhs_ty.variants();
assert_eq!(unfolded_lhs_variants.len(), unfolded_rhs_variants.len());
let mut folded_blocks = vec![];
for (
variant_index,
(
&EnumVariant {
name,
ty: unfolded_lhs_variant_ty,
},
unfolded_rhs_variant,
),
) in unfolded_lhs_variants
.iter()
.zip(&unfolded_rhs_variants)
.enumerate()
{
let mut output_stmts = vec![];
assert_eq!(name, unfolded_rhs_variant.name);
assert_eq!(
unfolded_lhs_variant_ty.is_some(),
unfolded_rhs_variant.ty.is_some()
);
let folded_variant_value =
if let (Some(unfolded_lhs_variant_ty), Some(unfolded_rhs_variant_ty)) =
(unfolded_lhs_variant_ty, unfolded_rhs_variant.ty)
{
let lhs_wire = Wire::new_unchecked(
self.module_state_stack
.last_mut()
.unwrap()
.gen_name("__connect_variant_body"),
source_location,
unfolded_lhs_variant_ty.fold(self)?,
);
output_stmts.push(
StmtWire {
annotations: Interned::default(),
wire: lhs_wire,
}
.into(),
);
let lhs_wire = lhs_wire.to_expr();
let folded_rhs_variant =
self.handle_variant_access(unfolded_rhs_ty, folded_rhs, variant_index)?;
self.handle_stmt_connect(
unfolded_lhs_variant_ty,
unfolded_rhs_variant_ty,
lhs_wire,
folded_rhs_variant,
source_location,
&mut output_stmts,
)?;
Some(lhs_wire)
} else {
None
};
output_stmts.push(
StmtConnect {
lhs: folded_lhs,
rhs: self.handle_enum_literal(
unfolded_lhs_ty,
variant_index,
folded_variant_value,
)?,
source_location,
}
.into(),
);
folded_blocks.push(Block {
memories: Interned::default(),
stmts: Intern::intern_owned(output_stmts),
});
}
output_stmts.push(self.handle_match(
unfolded_rhs_ty,
folded_rhs,
source_location,
&folded_blocks,
)?);
Ok(())
}
fn handle_stmt_connect(
&mut self,
unfolded_lhs_ty: CanonicalType,
unfolded_rhs_ty: CanonicalType,
folded_lhs: Expr<CanonicalType>,
folded_rhs: Expr<CanonicalType>,
source_location: SourceLocation,
output_stmts: &mut Vec<Stmt>,
) -> Result<(), SimplifyEnumsError> {
let needs_expansion = unfolded_lhs_ty != unfolded_rhs_ty
&& (contains_any_enum_types(unfolded_lhs_ty)
|| contains_any_enum_types(unfolded_rhs_ty));
if !needs_expansion {
output_stmts.push(
StmtConnect {
lhs: folded_lhs,
rhs: folded_rhs,
source_location,
}
.into(),
);
return Ok(());
}
match unfolded_lhs_ty {
CanonicalType::Array(unfolded_lhs_ty) => self.handle_stmt_connect_array(
unfolded_lhs_ty,
Array::from_canonical(unfolded_rhs_ty),
Expr::from_canonical(folded_lhs),
Expr::from_canonical(folded_rhs),
source_location,
output_stmts,
),
CanonicalType::Enum(unfolded_lhs_ty) => self.handle_stmt_connect_enum(
unfolded_lhs_ty,
Enum::from_canonical(unfolded_rhs_ty),
folded_lhs,
folded_rhs,
source_location,
output_stmts,
),
CanonicalType::Bundle(unfolded_lhs_ty) => self.handle_stmt_connect_bundle(
unfolded_lhs_ty,
Bundle::from_canonical(unfolded_rhs_ty),
Expr::from_canonical(folded_lhs),
Expr::from_canonical(folded_rhs),
source_location,
output_stmts,
),
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => unreachable!(),
}
}
} }
fn connect_port( fn connect_port(
@ -525,11 +117,11 @@ fn connect_port(
) { ) {
if Expr::ty(lhs) == Expr::ty(rhs) { if Expr::ty(lhs) == Expr::ty(rhs) {
stmts.push( stmts.push(
StmtConnect { dbg!(StmtConnect {
lhs, lhs,
rhs, rhs,
source_location, source_location,
} })
.into(), .into(),
); );
return; return;
@ -585,42 +177,6 @@ fn connect_port(
} }
} }
fn match_int_tag(
int_tag_expr: Expr<UInt>,
source_location: SourceLocation,
folded_blocks: &[Block],
) -> StmtIf {
let mut blocks_iter = folded_blocks.iter().copied().enumerate();
let (_, last_block) = blocks_iter.next_back().unwrap_or_default();
let Some((next_to_last_variant_index, next_to_last_block)) = blocks_iter.next_back() else {
return StmtIf {
cond: true.to_expr(),
source_location,
blocks: [last_block, Block::default()],
};
};
let mut retval = StmtIf {
cond: int_tag_expr
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)),
source_location,
blocks: [next_to_last_block, last_block],
};
for (variant_index, block) in blocks_iter.rev() {
retval = StmtIf {
cond: int_tag_expr.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)),
source_location,
blocks: [
block,
Block {
memories: Default::default(),
stmts: [Stmt::from(retval)][..].intern(),
},
],
};
}
retval
}
impl Folder for State { impl Folder for State {
type Error = SimplifyEnumsError; type Error = SimplifyEnumsError;
@ -629,32 +185,96 @@ 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 { let old_name_id_gen =
module_name: v.name_id(), std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical()));
});
let retval = Fold::default_fold(v, self); let retval = Fold::default_fold(v, self);
self.module_state_stack.pop(); self.name_id_gen = old_name_id_gen;
retval retval
} }
fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> { fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> {
match op { match op {
ExprEnum::EnumLiteral(op) => { ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? {
let folded_variant_value = op.variant_value().map(|v| v.fold(self)).transpose()?; EnumTypeState::TagEnumAndBody(TagAndBody { tag, body }) => *Expr::expr_enum(
Ok(*Expr::expr_enum(self.handle_enum_literal( <TagAndBody<Enum, DynSize> as BundleType>::Builder::default()
.field_tag(EnumLiteral::new_by_index(tag, op.variant_index(), None))
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => body.zero().to_expr(),
})
.to_expr(),
),
EnumTypeState::TagUIntAndBody(TagAndBody { tag, body }) => *Expr::expr_enum(
<TagAndBody<UInt, DynSize> as BundleType>::Builder::default()
.field_tag(tag.from_int_wrapping(op.variant_index()))
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => body.zero().to_expr(),
})
.to_expr(),
),
EnumTypeState::UInt(_) => *Expr::expr_enum(
<TagAndBody<UInt, DynSize> as BundleType>::Builder::default()
.field_tag(
UIntType::new(op.ty().discriminant_bit_width())
.from_int_wrapping(op.variant_index()),
)
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => UIntType::new(
op.ty().type_properties().bit_width
- op.ty().discriminant_bit_width(),
)
.zero()
.to_expr(),
})
.cast_to_bits(),
),
EnumTypeState::Unchanged => ExprEnum::EnumLiteral(ops::EnumLiteral::new_by_index(
op.ty(), op.ty(),
op.variant_index(), op.variant_index(),
folded_variant_value, op.variant_value().map(|v| v.fold(self)).transpose()?,
)?)) )),
} }),
ExprEnum::VariantAccess(op) => { ExprEnum::VariantAccess(op) => Ok(
let folded_base_expr = Expr::canonical(op.base()).fold(self)?; match self.get_or_make_enum_type_state(Expr::ty(op.base()))? {
Ok(*Expr::expr_enum(self.handle_variant_access( EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => {
Expr::ty(op.base()), match op.variant_type() {
folded_base_expr, Some(variant_type) => *Expr::expr_enum(
op.variant_index(), Expr::<TagAndBody<CanonicalType, DynSize>>::from_canonical(
)?)) (*Expr::expr_enum(op.base())).fold(self)?.to_expr(),
} )
.body[..variant_type.bit_width()]
.cast_bits_to(variant_type),
),
None => *Expr::expr_enum(().to_expr()),
}
}
EnumTypeState::UInt(_) => match op.variant_type() {
Some(variant_type) => {
let base_int = Expr::<UInt>::from_canonical(
(*Expr::expr_enum(op.base())).fold(self)?.to_expr(),
);
dbg!(base_int);
let base_ty = Expr::ty(op.base());
let variant_type_bit_width = variant_type.bit_width();
*Expr::expr_enum(
base_int[base_ty.discriminant_bit_width()..]
[..variant_type_bit_width]
.cast_bits_to(variant_type),
)
}
None => *Expr::expr_enum(().to_expr()),
},
EnumTypeState::Unchanged => match op.variant_type() {
Some(_) => ExprEnum::VariantAccess(ops::VariantAccess::new_by_index(
op.base().fold(self)?,
op.variant_index(),
)),
None => *Expr::expr_enum(().to_expr()),
},
},
),
ExprEnum::MemPort(mem_port) => Ok( ExprEnum::MemPort(mem_port) => Ok(
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)
@ -667,7 +287,6 @@ impl Folder for State {
| ExprEnum::BoolLiteral(_) | ExprEnum::BoolLiteral(_)
| ExprEnum::BundleLiteral(_) | ExprEnum::BundleLiteral(_)
| ExprEnum::ArrayLiteral(_) | ExprEnum::ArrayLiteral(_)
| ExprEnum::Uninit(_)
| ExprEnum::NotU(_) | ExprEnum::NotU(_)
| ExprEnum::NotS(_) | ExprEnum::NotS(_)
| ExprEnum::NotB(_) | ExprEnum::NotB(_)
@ -764,9 +383,7 @@ impl Folder for State {
| ExprEnum::ModuleIO(_) | ExprEnum::ModuleIO(_)
| ExprEnum::Instance(_) | ExprEnum::Instance(_)
| ExprEnum::Wire(_) | ExprEnum::Wire(_)
| ExprEnum::Reg(_) | ExprEnum::Reg(_) => op.default_fold(self),
| ExprEnum::RegSync(_)
| ExprEnum::RegAsync(_) => op.default_fold(self),
} }
} }
@ -800,15 +417,11 @@ impl Folder for State {
if wire_ty == new_port_ty { if wire_ty == new_port_ty {
continue; continue;
} }
let wire_name = self.name_id_gen.gen(
(*format!("{}_{}", memory.scoped_name().1 .0, port.port_name())).intern(),
);
let wire = Wire::new_unchecked( let wire = Wire::new_unchecked(
self.module_state_stack ScopedNameId(memory.scoped_name().0, wire_name),
.last_mut()
.unwrap()
.gen_name(&format!(
"{}_{}",
memory.scoped_name().1 .0,
port.port_name()
)),
port.source_location(), port.source_location(),
wire_ty, wire_ty,
); );
@ -851,50 +464,80 @@ impl Folder for State {
} }
fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> { fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> {
fn match_int_tag(
state: &mut State,
int_tag_expr: Expr<UInt>,
source_location: SourceLocation,
blocks: Interned<[Block]>,
) -> Result<StmtIf, SimplifyEnumsError> {
let mut blocks_iter = blocks.iter().copied().enumerate();
let (_, last_block) = blocks_iter.next_back().unwrap_or_default();
let Some((next_to_last_variant_index, next_to_last_block)) = blocks_iter.next_back()
else {
return Ok(StmtIf {
cond: true.to_expr(),
source_location,
blocks: [last_block.fold(state)?, Block::default()],
});
};
let mut retval = StmtIf {
cond: int_tag_expr
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)),
source_location,
blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?],
};
for (variant_index, block) in blocks_iter.rev() {
retval = StmtIf {
cond: int_tag_expr
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)),
source_location,
blocks: [
block.fold(state)?,
Block {
memories: Default::default(),
stmts: [Stmt::from(retval)][..].intern(),
},
],
};
}
Ok(retval)
}
match stmt { match stmt {
Stmt::Match(StmtMatch { Stmt::Match(StmtMatch {
expr, expr,
source_location, source_location,
blocks, blocks,
}) => { }) => match self.get_or_make_enum_type_state(Expr::ty(expr))? {
let folded_expr = Expr::canonical(expr).fold(self)?; EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch {
let folded_blocks = blocks.fold(self)?; expr: Expr::<TagAndBody<Enum, DynSize>>::from_canonical(
self.handle_match(Expr::ty(expr), folded_expr, source_location, &folded_blocks) Expr::canonical(expr).fold(self)?,
} )
Stmt::Connect(StmtConnect { .tag,
lhs,
rhs,
source_location,
}) => {
let folded_lhs = lhs.fold(self)?;
let folded_rhs = rhs.fold(self)?;
let mut output_stmts = vec![];
self.handle_stmt_connect(
Expr::ty(lhs),
Expr::ty(rhs),
folded_lhs,
folded_rhs,
source_location, source_location,
&mut output_stmts, blocks: blocks.fold(self)?,
)?;
if output_stmts.len() == 1 {
Ok(output_stmts.pop().unwrap())
} else {
Ok(StmtIf {
cond: true.to_expr(),
source_location,
blocks: [
Block {
memories: Interned::default(),
stmts: Intern::intern_owned(output_stmts),
},
Block::default(),
],
}
.into())
} }
} .into()),
Stmt::Formal(_) | Stmt::If(_) | Stmt::Declaration(_) => stmt.default_fold(self), EnumTypeState::TagUIntAndBody(_) => {
let int_tag_expr = Expr::<TagAndBody<UInt, DynSize>>::from_canonical(
Expr::canonical(expr).fold(self)?,
)
.tag;
Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
}
EnumTypeState::UInt(_) => {
let int_tag_expr =
Expr::<UInt>::from_canonical(Expr::canonical(expr).fold(self)?)
[..Expr::ty(expr).discriminant_bit_width()];
Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
}
EnumTypeState::Unchanged => Ok(StmtMatch {
expr: expr.fold(self)?,
source_location,
blocks: blocks.fold(self)?,
}
.into()),
},
Stmt::Connect(_) | Stmt::If(_) | Stmt::Declaration(_) => stmt.default_fold(self),
} }
} }
@ -931,10 +574,13 @@ impl Folder for State {
unreachable!() unreachable!()
} }
fn fold_enum_literal<T: EnumType + Fold<Self>>( fn fold_enum_literal<T: EnumType>(
&mut self, &mut self,
_v: ops::EnumLiteral<T>, _v: ops::EnumLiteral<T>,
) -> Result<ops::EnumLiteral<T>, Self::Error> { ) -> Result<ops::EnumLiteral<T>, Self::Error>
where
T: Fold<Self>,
{
unreachable!() unreachable!()
} }
@ -946,12 +592,10 @@ impl Folder for State {
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum SimplifyEnumsKind { pub enum SimplifyEnumsKind {
SimplifyToEnumsWithNoBody, SimplifyToEnumsWithNoBody,
#[clap(name = "replace-with-bundle-of-uints")]
ReplaceWithBundleOfUInts, ReplaceWithBundleOfUInts,
#[clap(name = "replace-with-uint")]
ReplaceWithUInt, ReplaceWithUInt,
} }
@ -963,6 +607,6 @@ pub fn simplify_enums(
enum_types: HashMap::new(), enum_types: HashMap::new(),
replacement_mem_ports: HashMap::new(), replacement_mem_ports: HashMap::new(),
kind, kind,
module_state_stack: vec![], name_id_gen: NameIdGen::default(),
}) })
} }

View file

@ -10,7 +10,7 @@ use crate::{
memory::{Mem, MemPort, PortType}, memory::{Mem, MemPort, PortType},
module::{ module::{
transform::visit::{Fold, Folder}, transform::visit::{Fold, Folder},
Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtWire, Block, Module, NameId, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtWire,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
@ -417,6 +417,7 @@ impl SplitMemState<'_, '_> {
struct ModuleState { struct ModuleState {
output_module: Option<Interned<Module<Bundle>>>, output_module: Option<Interned<Module<Bundle>>>,
name_id_gen: NameIdGen,
memories: HashMap<ScopedNameId, MemState>, memories: HashMap<ScopedNameId, MemState>,
} }
@ -568,7 +569,7 @@ impl ModuleState {
port_wmask.map(Expr::from_canonical), port_wmask.map(Expr::from_canonical),
connect_read_enum, connect_read_enum,
connect_write_enum, connect_write_enum,
connect_write, connect_write_enum,
), ),
CanonicalType::Array(array_type) => { CanonicalType::Array(array_type) => {
input_array_types.push(array_type); input_array_types.push(array_type);
@ -625,10 +626,10 @@ impl ModuleState {
mem_name_path: &str, mem_name_path: &str,
split_state: &SplitState<'_>, split_state: &SplitState<'_>,
) -> Mem { ) -> Mem {
let mem_name = NameId( let mem_name = self.name_id_gen.gen(Intern::intern_owned(format!(
Intern::intern_owned(format!("{}{mem_name_path}", input_mem.scoped_name().1 .0)), "{}{mem_name_path}",
Id::new(), input_mem.scoped_name().1 .0
); )));
let mem_name = ScopedNameId(input_mem.scoped_name().0, mem_name); let mem_name = ScopedNameId(input_mem.scoped_name().0, mem_name);
let output_element_type = match single_type { let output_element_type = match single_type {
SingleType::UInt(ty) => ty.canonical(), SingleType::UInt(ty) => ty.canonical(),
@ -752,10 +753,9 @@ impl ModuleState {
let port_ty = port.ty(); let port_ty = port.ty();
let NameId(mem_name, _) = input_mem.scoped_name().1; let NameId(mem_name, _) = input_mem.scoped_name().1;
let port_name = port.port_name(); let port_name = port.port_name();
let wire_name = NameId( let wire_name = self
Intern::intern_owned(format!("{mem_name}_{port_name}")), .name_id_gen
Id::new(), .gen(Intern::intern_owned(format!("{mem_name}_{port_name}")));
);
let wire = Wire::new_unchecked( let wire = Wire::new_unchecked(
ScopedNameId(input_mem.scoped_name().0, wire_name), ScopedNameId(input_mem.scoped_name().0, wire_name),
port.source_location(), port.source_location(),
@ -766,7 +766,7 @@ impl ModuleState {
output_stmts.push( output_stmts.push(
StmtWire { StmtWire {
annotations: Default::default(), annotations: Default::default(),
wire: canonical_wire, wire: canonical_wire.clone(),
} }
.into(), .into(),
); );
@ -887,6 +887,7 @@ impl Folder for State {
module, module,
ModuleState { ModuleState {
output_module: None, output_module: None,
name_id_gen: NameIdGen::for_module(*module),
memories: HashMap::new(), memories: HashMap::new(),
}, },
); );

View file

@ -2,10 +2,7 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
#![allow(clippy::multiple_bound_locations)] #![allow(clippy::multiple_bound_locations)]
use crate::{ use crate::{
annotations::{ annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation},
Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation,
},
array::ArrayType, array::ArrayType,
bundle::{Bundle, BundleField, BundleType}, bundle::{Bundle, BundleField, BundleType},
clock::Clock, clock::Clock,
@ -18,18 +15,17 @@ use crate::{
}, },
Expr, ExprEnum, Expr, ExprEnum,
}, },
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, ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId,
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtIf, StmtInstance,
StmtInstance, StmtMatch, StmtReg, StmtWire, StmtMatch, StmtReg, StmtWire,
}, },
reg::Reg, reg::Reg,
reset::{AsyncReset, Reset, ResetType, SyncReset}, reset::{AsyncReset, Reset, SyncReset},
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
wire::Wire, wire::Wire,

View file

@ -1,30 +1,16 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
pub use crate::{ pub use crate::{
annotations::{ annotations::Annotation,
BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
},
array::{Array, ArrayType}, array::{Array, ArrayType},
bundle::Bundle,
cli::Cli, cli::Cli,
clock::{Clock, ClockDomain, ToClock}, clock::{Clock, ClockDomain, ToClock},
enum_::{Enum, HdlNone, HdlOption, HdlSome}, enum_::{HdlNone, HdlOption, HdlSome},
expr::{ expr::{CastBitsTo, CastTo, CastToBits, Expr, ReduceBits, ToExpr},
repeat, CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr,
ReduceBits, ToExpr,
},
formal::{
all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, hdl_assert,
hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover,
hdl_cover_with_enable, MakeFormalExpr,
},
hdl, hdl_module, hdl, hdl_module,
int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType}, int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType},
memory::{Mem, MemBuilder, ReadUnderWrite}, memory::{Mem, MemBuilder, ReadUnderWrite},
module::{ module::{
annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, annotate, connect, connect_any, instance, memory, memory_array, memory_with_init,
memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, reg_builder, wire, Instance, Module, ModuleBuilder,
}, },
reg::Reg, reg::Reg,
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},

View file

@ -5,22 +5,21 @@ use crate::{
expr::{Expr, Flow}, expr::{Expr, Flow},
intern::Interned, intern::Interned,
module::{NameId, ScopedNameId}, module::{NameId, ScopedNameId},
reset::{Reset, ResetType},
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
}; };
use std::fmt; use std::fmt;
#[derive(Copy, Clone, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Reg<T: Type, R: ResetType = Reset> { pub struct Reg<T: Type> {
name: ScopedNameId, name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
ty: T, ty: T,
clock_domain: Expr<ClockDomain<R>>, clock_domain: Expr<ClockDomain>,
init: Option<Expr<T>>, init: Option<Expr<T>>,
} }
impl<T: Type + fmt::Debug, R: ResetType> fmt::Debug for Reg<T, R> { impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { let Self {
name, name,
@ -38,8 +37,8 @@ impl<T: Type + fmt::Debug, R: ResetType> fmt::Debug for Reg<T, R> {
} }
} }
impl<T: Type, R: ResetType> Reg<T, R> { impl<T: Type> Reg<T> {
pub fn canonical(&self) -> Reg<CanonicalType, R> { pub fn canonical(&self) -> Reg<CanonicalType> {
let Self { let Self {
name, name,
source_location, source_location,
@ -60,7 +59,7 @@ impl<T: Type, R: ResetType> Reg<T, R> {
scoped_name: ScopedNameId, scoped_name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
ty: T, ty: T,
clock_domain: Expr<ClockDomain<R>>, clock_domain: Expr<ClockDomain>,
init: Option<Expr<T>>, init: Option<Expr<T>>,
) -> Self { ) -> Self {
assert!( assert!(
@ -99,7 +98,7 @@ impl<T: Type, R: ResetType> Reg<T, R> {
pub fn scoped_name(&self) -> ScopedNameId { pub fn scoped_name(&self) -> ScopedNameId {
self.name self.name
} }
pub fn clock_domain(&self) -> Expr<ClockDomain<R>> { pub fn clock_domain(&self) -> Expr<ClockDomain> {
self.clock_domain self.clock_domain
} }
pub fn init(&self) -> Option<Expr<T>> { pub fn init(&self) -> Option<Expr<T>> {

View file

@ -1,9 +1,8 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
clock::Clock, expr::{Expr, ToExpr},
expr::{ops, Expr, ToExpr}, int::Bool,
int::{Bool, SInt, UInt},
source_location::SourceLocation, source_location::SourceLocation,
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
}; };
@ -12,33 +11,10 @@ mod sealed {
pub trait ResetTypeSealed {} pub trait ResetTypeSealed {}
} }
pub trait ResetType: pub trait ResetType: StaticType<MaskType = Bool> + sealed::ResetTypeSealed {}
StaticType<MaskType = Bool>
+ sealed::ResetTypeSealed
+ ops::ExprCastTo<Bool>
+ ops::ExprCastTo<Reset>
+ ops::ExprCastTo<SyncReset>
+ ops::ExprCastTo<AsyncReset>
+ ops::ExprCastTo<Clock>
+ ops::ExprCastTo<UInt<1>>
+ ops::ExprCastTo<SInt<1>>
+ ops::ExprCastTo<UInt>
+ ops::ExprCastTo<SInt>
{
fn dispatch<D: ResetTypeDispatch>(input: D::Input<Self>, dispatch: D) -> D::Output<Self>;
}
pub trait ResetTypeDispatch: Sized {
type Input<T: ResetType>;
type Output<T: ResetType>;
fn reset(self, input: Self::Input<Reset>) -> Self::Output<Reset>;
fn sync_reset(self, input: Self::Input<SyncReset>) -> Self::Output<SyncReset>;
fn async_reset(self, input: Self::Input<AsyncReset>) -> Self::Output<AsyncReset>;
}
macro_rules! reset_type { macro_rules! reset_type {
($name:ident, $(#[$impl_trait:ident])? $Trait:ident::$trait_fn:ident, $is_castable_from_bits:literal, $dispatch_fn:ident) => { ($name:ident, $Trait:ident::$trait_fn:ident, $is_castable_from_bits:literal) => {
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct $name; pub struct $name;
@ -91,14 +67,7 @@ macro_rules! reset_type {
impl sealed::ResetTypeSealed for $name {} impl sealed::ResetTypeSealed for $name {}
impl ResetType for $name { impl ResetType for $name {}
fn dispatch<D: ResetTypeDispatch>(
input: D::Input<Self>,
dispatch: D,
) -> D::Output<Self> {
dispatch.$dispatch_fn(input)
}
}
pub trait $Trait { pub trait $Trait {
fn $trait_fn(&self) -> Expr<$name>; fn $trait_fn(&self) -> Expr<$name>;
@ -122,21 +91,20 @@ macro_rules! reset_type {
} }
} }
$($impl_trait $Trait for Expr<$name> { impl $Trait for Expr<$name> {
fn $trait_fn(&self) -> Expr<$name> { fn $trait_fn(&self) -> Expr<$name> {
*self *self
} }
})? }
}; };
} }
reset_type!(AsyncReset, #[impl] ToAsyncReset::to_async_reset, true, async_reset); reset_type!(AsyncReset, ToAsyncReset::to_async_reset, true);
reset_type!(SyncReset, #[impl] ToSyncReset::to_sync_reset, true, sync_reset); reset_type!(SyncReset, ToSyncReset::to_sync_reset, true);
reset_type!( reset_type!(
Reset, Reset,
ToReset::to_reset, ToReset::to_reset,
false, // Reset is not castable from bits because we don't know if it's async or sync false // Reset is not castable from bits because we don't know if it's async or sync
reset
); );
impl ToSyncReset for bool { impl ToSyncReset for bool {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,397 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::{
fmt,
ops::{Add, AddAssign, Sub, SubAssign},
time::Duration,
};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct SimInstant {
time_since_start: SimDuration,
}
impl SimInstant {
pub const fn checked_add(self, duration: SimDuration) -> Option<Self> {
let Some(time_since_start) = self.time_since_start.checked_add(duration) else {
return None;
};
Some(SimInstant { time_since_start })
}
pub const fn checked_duration_since(self, earlier: Self) -> Option<SimDuration> {
self.time_since_start.checked_sub(earlier.time_since_start)
}
pub const fn checked_sub(self, duration: SimDuration) -> Option<Self> {
let Some(time_since_start) = self.time_since_start.checked_sub(duration) else {
return None;
};
Some(SimInstant { time_since_start })
}
#[track_caller]
pub const fn duration_since(self, earlier: Self) -> SimDuration {
let Some(retval) = self.checked_duration_since(earlier) else {
panic!(
"tried to compute the duration since a later time -- durations can't be negative"
);
};
retval
}
pub const fn saturating_duration_since(self, earlier: Self) -> SimDuration {
let Some(retval) = self.checked_duration_since(earlier) else {
return SimDuration::ZERO;
};
retval
}
}
impl Add<SimDuration> for SimInstant {
type Output = SimInstant;
#[track_caller]
fn add(mut self, rhs: SimDuration) -> Self::Output {
self += rhs;
self
}
}
impl AddAssign<SimDuration> for SimInstant {
#[track_caller]
fn add_assign(&mut self, rhs: SimDuration) {
self.time_since_start += rhs;
}
}
impl Add<SimInstant> for SimDuration {
type Output = SimInstant;
#[track_caller]
fn add(self, rhs: SimInstant) -> Self::Output {
rhs.add(self)
}
}
impl Sub for SimInstant {
type Output = SimDuration;
#[track_caller]
fn sub(self, rhs: SimInstant) -> Self::Output {
self.duration_since(rhs)
}
}
impl Sub<SimDuration> for SimInstant {
type Output = SimInstant;
#[track_caller]
fn sub(self, rhs: SimDuration) -> Self::Output {
let Some(retval) = self.checked_sub(rhs) else {
panic!("SimInstant underflow");
};
retval
}
}
impl SubAssign<SimDuration> for SimInstant {
#[track_caller]
fn sub_assign(&mut self, rhs: SimDuration) {
*self = *self - rhs;
}
}
impl SimInstant {
pub const START: SimInstant = SimInstant {
time_since_start: SimDuration::ZERO,
};
}
impl fmt::Debug for SimInstant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.time_since_start.fmt(f)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct SimDuration {
attos: u128,
}
impl AddAssign for SimDuration {
#[track_caller]
fn add_assign(&mut self, rhs: SimDuration) {
*self = *self + rhs;
}
}
impl Add for SimDuration {
type Output = SimDuration;
#[track_caller]
fn add(self, rhs: SimDuration) -> Self::Output {
SimDuration {
attos: self
.attos
.checked_add(rhs.attos)
.expect("overflow adding durations"),
}
}
}
impl Sub for SimDuration {
type Output = Self;
#[track_caller]
fn sub(self, rhs: Self) -> Self::Output {
SimDuration {
attos: self
.attos
.checked_add(rhs.attos)
.expect("underflow subtracting durations -- durations can't be negative"),
}
}
}
impl SubAssign for SimDuration {
#[track_caller]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct SimDurationParts {
pub attos: u16,
pub femtos: u16,
pub picos: u16,
pub nanos: u16,
pub micros: u16,
pub millis: u16,
pub secs: u128,
}
macro_rules! impl_duration_units {
(
$(
#[unit_const = $UNIT:ident, from_units = $from_units:ident, as_units = $as_units:ident, units = $units:ident, suffix = $suffix:literal]
const $log10_units_per_sec:ident: u32 = $log10_units_per_sec_value:expr;
)*
) => {
impl SimDuration {
$(
const $log10_units_per_sec: u32 = $log10_units_per_sec_value;
pub const fn $from_units($units: u128) -> Self {
Self::from_units_helper::<{ Self::$log10_units_per_sec }>($units)
}
pub const fn $as_units(self) -> u128 {
self.attos / const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) }
}
)*
pub const fn to_parts(mut self) -> SimDurationParts {
$(
let $units = self.attos / const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
self.attos %= const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
)*
SimDurationParts {
$($units: $units as _,)*
}
}
pub const fn from_parts_checked(parts: SimDurationParts) -> Option<Self> {
let attos = 0u128;
$(
let Some(product) = const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) }.checked_mul(parts.$units as u128) else {
return None;
};
let Some(attos) = attos.checked_add(product) else {
return None;
};
)*
Some(Self {
attos,
})
}
}
impl fmt::Debug for SimDuration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ilog10_attos = match self.attos.checked_ilog10() {
Some(v) => v,
None => Self::LOG10_ATTOS_PER_SEC,
};
let (suffix, int, fraction, fraction_digits) =
match Self::LOG10_ATTOS_PER_SEC.saturating_sub(ilog10_attos) {
$(
..=Self::$log10_units_per_sec => {
let divisor = const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
(
$suffix,
self.attos / divisor,
self.attos % divisor,
(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) as usize,
)
},
)*
_ => unreachable!(),
};
write!(f, "{int}")?;
if fraction != 0 {
write!(f, ".{fraction:0fraction_digits$}")?;
}
write!(f, " {suffix}")
}
}
#[cfg(test)]
#[test]
fn test_duration_debug() {
$(
assert_eq!(
format!("{:?}", SimDuration::$from_units(123)),
concat!("123 ", $suffix)
);
assert_eq!(
format!("{:?}", SimDuration::$from_units(1)),
concat!("1 ", $suffix),
);
let mut v = SimDuration::$from_units(1);
if v.attos < 1 << 53 {
v.attos += 1;
assert_eq!(
format!("{v:?}"),
format!("{} {}", v.attos as f64 / 10.0f64.powf((SimDuration::LOG10_ATTOS_PER_SEC - SimDuration::$log10_units_per_sec) as f64), $suffix),
"1 {} + 1 as == {} as", $suffix, v.attos,
);
}
)*
}
};
}
impl_duration_units! {
#[unit_const = SECOND, from_units = from_secs, as_units = as_secs, units = secs, suffix = "s"]
const LOG10_SECS_PER_SEC: u32 = 0;
#[unit_const = MILLISECOND, from_units = from_millis, as_units = as_millis, units = millis, suffix = "ms"]
const LOG10_MILLIS_PER_SEC: u32 = 3;
#[unit_const = MICROSECOND, from_units = from_micros, as_units = as_micros, units = micros, suffix = "μs"]
const LOG10_MICROS_PER_SEC: u32 = 6;
#[unit_const = NANOSECOND, from_units = from_nanos, as_units = as_nanos, units = nanos, suffix = "ns"]
const LOG10_NANOS_PER_SEC: u32 = 9;
#[unit_const = PICOSECOND, from_units = from_picos, as_units = as_picos, units = picos, suffix = "ps"]
const LOG10_PICOS_PER_SEC: u32 = 12;
#[unit_const = FEMTOSECOND, from_units = from_femtos, as_units = as_femtos, units = femtos, suffix = "fs"]
const LOG10_FEMTOS_PER_SEC: u32 = 15;
#[unit_const = ATTOSECOND, from_units = from_attos, as_units = as_attos, units = attos, suffix = "as"]
const LOG10_ATTOS_PER_SEC: u32 = 18;
}
impl SimDuration {
const fn from_units_helper<const UNITS_PER_SEC: u32>(units: u128) -> Self {
let Some(attos) =
units.checked_mul(const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - UNITS_PER_SEC) })
else {
panic!("duration too big");
};
Self { attos }
}
pub const ZERO: SimDuration = SimDuration::from_secs(0);
pub const fn from_parts(parts: SimDurationParts) -> Self {
match Self::from_parts_checked(parts) {
Some(v) => v,
None => panic!("duration too big"),
}
}
pub const fn abs_diff(self, other: Self) -> Self {
Self {
attos: self.attos.abs_diff(other.attos),
}
}
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
let Some(attos) = self.attos.checked_add(rhs.attos) else {
return None;
};
Some(Self { attos })
}
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
let Some(attos) = self.attos.checked_sub(rhs.attos) else {
return None;
};
Some(Self { attos })
}
pub const fn is_zero(self) -> bool {
self.attos == 0
}
pub const fn saturating_add(self, rhs: Self) -> Self {
Self {
attos: self.attos.saturating_add(rhs.attos),
}
}
pub const fn saturating_sub(self, rhs: Self) -> Self {
Self {
attos: self.attos.saturating_sub(rhs.attos),
}
}
pub const fn checked_ilog10(self) -> Option<i32> {
let Some(ilog10_attos) = self.attos.checked_ilog10() else {
return None;
};
Some(ilog10_attos as i32 - Self::LOG10_ATTOS_PER_SEC as i32)
}
#[track_caller]
pub const fn ilog10(self) -> i32 {
let Some(retval) = self.checked_ilog10() else {
panic!("tried to take the ilog10 of 0");
};
retval
}
pub const fn checked_pow10(log10: i32, underflow_is_zero: bool) -> Option<Self> {
let Some(log10) = Self::LOG10_ATTOS_PER_SEC.checked_add_signed(log10) else {
return if log10 < 0 && underflow_is_zero {
Some(Self::ZERO)
} else {
None
};
};
let Some(attos) = 10u128.checked_pow(log10) else {
return None;
};
Some(Self { attos })
}
#[track_caller]
pub const fn pow10(log10: i32) -> Self {
let Some(retval) = Self::checked_pow10(log10, true) else {
panic!("pow10 overflowed");
};
retval
}
pub const fn is_power_of_ten(self) -> bool {
const TEN: u128 = 10;
const NUMBER_OF_POWERS_OF_TEN: usize = {
let mut n = 0;
while let Some(_) = TEN.checked_pow(n as u32) {
n += 1;
}
n
};
const POWERS_OF_TEN: [u128; NUMBER_OF_POWERS_OF_TEN] = {
let mut retval = [0; NUMBER_OF_POWERS_OF_TEN];
let mut i = 0;
while i < NUMBER_OF_POWERS_OF_TEN {
retval[i] = TEN.pow(i as u32);
i += 1;
}
retval
};
let mut i = 0;
while i < NUMBER_OF_POWERS_OF_TEN {
if self.attos == POWERS_OF_TEN[i] {
return true;
}
i += 1;
}
false
}
}
impl From<Duration> for SimDuration {
fn from(duration: Duration) -> Self {
Self::from_nanos(duration.as_nanos())
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,122 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
cli::{FormalArgs, FormalMode, FormalOutput, RunPhase},
firrtl::ExportOptions,
};
use clap::Parser;
use hashbrown::HashMap;
use serde::Deserialize;
use std::{
fmt::Write,
path::{Path, PathBuf},
process::Command,
sync::{Mutex, OnceLock},
};
fn assert_formal_helper() -> FormalArgs {
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
FORMAL_ARGS
.get_or_init(|| FormalArgs::parse_from(["fayalite::testing::assert_formal"]))
.clone()
}
#[derive(Deserialize)]
struct CargoMetadata {
target_directory: String,
}
fn get_cargo_target_dir() -> &'static Path {
static RETVAL: OnceLock<PathBuf> = OnceLock::new();
RETVAL.get_or_init(|| {
let output = Command::new(
std::env::var_os("CARGO")
.as_deref()
.unwrap_or("cargo".as_ref()),
)
.arg("metadata")
.output()
.expect("can't run `cargo metadata`");
if !output.status.success() {
panic!(
"can't run `cargo metadata`:\n{}\nexited with status: {}",
String::from_utf8_lossy(&output.stderr),
output.status
);
}
let CargoMetadata { target_directory } =
serde_json::from_slice(&output.stdout).expect("can't parse output of `cargo metadata`");
PathBuf::from(target_directory)
})
}
#[track_caller]
fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf {
static DIRS: Mutex<Option<HashMap<String, u64>>> = Mutex::new(None);
let test_name = test_name.to_string();
// don't use line/column numbers since that constantly changes as you edit tests
let file = std::panic::Location::caller().file();
// simple reproducible hash
let simple_hash = file.bytes().chain(test_name.bytes()).fold(
((file.len() as u32) << 16).wrapping_add(test_name.len() as u32),
|mut h, b| {
h = h.wrapping_mul(0xaa0d184b);
h ^= h.rotate_right(5);
h ^= h.rotate_right(13);
h.wrapping_add(b as u32)
},
);
let mut dir = String::with_capacity(64);
for ch in Path::new(file)
.file_stem()
.unwrap_or_default()
.to_str()
.unwrap()
.chars()
.chain(['-'])
.chain(test_name.chars())
{
dir.push(match ch {
ch if ch.is_alphanumeric() => ch,
'_' | '-' | '+' | '.' | ',' | ' ' => ch,
_ => '_',
});
}
write!(dir, "-{simple_hash:08x}").unwrap();
let index = *DIRS
.lock()
.unwrap()
.get_or_insert_with(HashMap::new)
.entry_ref(&dir)
.and_modify(|v| *v += 1)
.or_insert(0);
write!(dir, "-{index}").unwrap();
get_cargo_target_dir()
.join("fayalite_assert_formal")
.join(dir)
}
#[track_caller]
pub fn assert_formal<M>(
test_name: impl std::fmt::Display,
module: M,
mode: FormalMode,
depth: u64,
solver: Option<&str>,
export_options: ExportOptions,
) where
FormalArgs: RunPhase<M, Output = FormalOutput>,
{
let mut args = assert_formal_helper();
args.verilog.firrtl.base.redirect_output_for_rust_test = true;
args.verilog.firrtl.base.output = Some(get_assert_formal_target_path(&test_name));
args.verilog.firrtl.export_options = export_options;
args.verilog.debug = true;
args.mode = mode;
args.depth = depth;
if let Some(solver) = solver {
args.solver = solver.into();
}
args.run(module).expect("testing::assert_formal() failed");
}

View file

@ -210,9 +210,7 @@ impl sealed::BaseTypeSealed for CanonicalType {}
impl BaseType for CanonicalType {} impl BaseType for CanonicalType {}
pub trait TypeOrDefault<D: Type>: pub trait TypeOrDefault<D: Type>: sealed::TypeOrDefaultSealed {
sealed::TypeOrDefaultSealed + Copy + Eq + Hash + fmt::Debug
{
type Type: Type; type Type: Type;
fn get<F: FnOnce() -> D>(self, default: F) -> Self::Type; fn get<F: FnOnce() -> D>(self, default: F) -> Self::Type;
} }
@ -330,6 +328,6 @@ impl<T: Type> Index<T> for AsMaskWithoutGenerics {
type Output = T::MaskType; type Output = T::MaskType;
fn index(&self, ty: T) -> &Self::Output { fn index(&self, ty: T) -> &Self::Output {
Interned::into_inner(Intern::intern_sized(ty.mask_type())) Interned::<_>::into_inner(Intern::intern_sized(ty.mask_type()))
} }
} }

View file

@ -6,7 +6,6 @@ mod const_cmp;
mod const_usize; mod const_usize;
mod misc; mod misc;
mod scoped_ref; mod scoped_ref;
pub(crate) mod streaming_read_utf8;
#[doc(inline)] #[doc(inline)]
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
@ -24,9 +23,5 @@ pub use scoped_ref::ScopedRef;
#[doc(inline)] #[doc(inline)]
pub use misc::{ pub use misc::{
get_many_mut, interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice,
DebugAsRawString, MakeMutSlice, RcWriter,
}; };
pub mod job_server;
pub mod ready_valid;

View file

@ -1,193 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use ctor::ctor;
use jobslot::{Acquired, Client};
use std::{
ffi::OsString,
mem,
num::NonZeroUsize,
sync::{Condvar, Mutex, Once, OnceLock},
thread::spawn,
};
fn get_or_make_client() -> &'static Client {
#[ctor]
static CLIENT: OnceLock<Client> = unsafe {
match Client::from_env() {
Some(client) => OnceLock::from(client),
None => OnceLock::new(),
}
};
CLIENT.get_or_init(|| {
let mut available_parallelism = None;
let mut args = std::env::args_os().skip(1);
while let Some(arg) = args.next() {
const TEST_THREADS_OPTION: &'static [u8] = b"--test-threads";
if arg.as_encoded_bytes().starts_with(TEST_THREADS_OPTION) {
match arg.as_encoded_bytes().get(TEST_THREADS_OPTION.len()) {
Some(b'=') => {
let mut arg = arg.into_encoded_bytes();
arg.drain(..=TEST_THREADS_OPTION.len());
available_parallelism = Some(arg);
break;
}
None => {
available_parallelism = args.next().map(OsString::into_encoded_bytes);
break;
}
_ => {}
}
}
}
let available_parallelism = if let Some(available_parallelism) = available_parallelism
.as_deref()
.and_then(|v| std::str::from_utf8(v).ok())
.and_then(|v| v.parse().ok())
{
available_parallelism
} else if let Ok(available_parallelism) = std::thread::available_parallelism() {
available_parallelism
} else {
NonZeroUsize::new(1).unwrap()
};
Client::new_with_fifo(available_parallelism.get() - 1).expect("failed to create job server")
})
}
struct State {
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 {
waiting_count: 0,
available: Vec::new(),
implicit_available: true,
});
static COND_VAR: Condvar = Condvar::new();
#[derive(Debug)]
enum AcquiredJobInner {
FromJobServer(Acquired),
ImplicitJob,
}
#[derive(Debug)]
pub struct AcquiredJob {
job: AcquiredJobInner,
}
impl AcquiredJob {
fn start_acquire_thread() {
static STARTED_THREAD: Once = Once::new();
STARTED_THREAD.call_once(|| {
spawn(|| {
let mut acquired = None;
let client = get_or_make_client();
let mut state = STATE.lock().unwrap();
loop {
state = if state.additional_waiting() == 0 {
if acquired.is_some() {
drop(state);
drop(acquired.take()); // drop Acquired outside of lock
STATE.lock().unwrap()
} else {
COND_VAR.wait(state).unwrap()
}
} else if acquired.is_some() {
// allocate space before moving Acquired to ensure we
// drop Acquired outside of the lock on panic
state.available.reserve(1);
state.available.push(acquired.take().unwrap());
COND_VAR.notify_all();
state
} else {
drop(state);
acquired = Some(
client
.acquire()
.expect("can't acquire token from job server"),
);
STATE.lock().unwrap()
};
}
});
});
}
fn acquire_inner(block: bool) -> Option<Self> {
Self::start_acquire_thread();
let mut state = STATE.lock().unwrap();
loop {
if let Some(acquired) = state.available.pop() {
return Some(Self {
job: AcquiredJobInner::FromJobServer(acquired),
});
}
if state.implicit_available {
state.implicit_available = false;
return Some(Self {
job: AcquiredJobInner::ImplicitJob,
});
}
if !block {
return None;
}
state.waiting_count += 1;
state = COND_VAR.wait(state).unwrap();
state.waiting_count -= 1;
}
}
pub fn try_acquire() -> Option<Self> {
Self::acquire_inner(false)
}
pub fn acquire() -> Self {
Self::acquire_inner(true).expect("failed to acquire token")
}
pub fn run_command<R>(
&mut self,
cmd: std::process::Command,
f: impl FnOnce(&mut std::process::Command) -> std::io::Result<R>,
) -> std::io::Result<R> {
get_or_make_client().configure_make_and_run_with_fifo(cmd, f)
}
}
impl Drop for AcquiredJob {
fn drop(&mut self) {
let mut state = STATE.lock().unwrap();
match &self.job {
AcquiredJobInner::FromJobServer(_) => {
if state.waiting_count > state.available.len() + state.implicit_available as usize {
// allocate space before moving Acquired to ensure we
// drop Acquired outside of the lock on panic
state.available.reserve(1);
let AcquiredJobInner::FromJobServer(acquired) =
mem::replace(&mut self.job, AcquiredJobInner::ImplicitJob)
else {
unreachable!()
};
state.available.push(acquired);
COND_VAR.notify_all();
}
}
AcquiredJobInner::ImplicitJob => {
state.implicit_available = true;
if state.waiting_count > state.available.len() {
COND_VAR.notify_all();
}
}
}
}
}

View file

@ -3,7 +3,6 @@
use crate::intern::{Intern, Interned}; 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,
fmt::{self, Debug, Write}, fmt::{self, Debug, Write},
rc::Rc, rc::Rc,
sync::{Arc, OnceLock}, sync::{Arc, OnceLock},
@ -95,15 +94,9 @@ pub fn interned_bit(v: bool) -> Interned<BitSlice> {
RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize] RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize]
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice); pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice);
impl<'a> Debug for BitSliceWriteWithBase<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:#x}")
}
}
impl BitSliceWriteWithBase<'_> { impl BitSliceWriteWithBase<'_> {
fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>( fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>(
self, self,
@ -162,66 +155,3 @@ impl fmt::UpperHex for BitSliceWriteWithBase<'_> {
self.fmt_with_base::<4, true>(f) self.fmt_with_base::<4, true>(f)
} }
} }
#[inline]
#[track_caller]
pub fn get_many_mut<T, const N: usize>(slice: &mut [T], indexes: [usize; N]) -> [&mut T; N] {
for i in 0..N {
for j in 0..i {
assert!(indexes[i] != indexes[j], "duplicate index");
}
assert!(indexes[i] < slice.len(), "index out of bounds");
}
// Safety: checked that no indexes are duplicates and no indexes are out of bounds
unsafe {
let base = slice.as_mut_ptr(); // convert to a raw pointer before loop to avoid aliasing with &mut [T]
std::array::from_fn(|i| &mut *base.add(indexes[i]))
}
}
#[derive(Clone, Default)]
pub struct RcWriter(Rc<Cell<Vec<u8>>>);
impl Debug for RcWriter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.borrow_impl(|buf| {
f.debug_tuple("RcWriter")
.field(&DebugAsDisplay(format_args!("b\"{}\"", buf.escape_ascii())))
.finish()
})
}
}
impl RcWriter {
fn borrow_impl<R>(&self, f: impl FnOnce(&mut Vec<u8>) -> R) -> R {
let buf = Cell::take(&self.0);
struct PutBackOnDrop<'a> {
buf: Vec<u8>,
this: &'a RcWriter,
}
impl Drop for PutBackOnDrop<'_> {
fn drop(&mut self) {
self.this.0.set(std::mem::take(&mut self.buf));
}
}
let mut buf = PutBackOnDrop { buf, this: self };
f(&mut buf.buf)
}
pub fn borrow<R>(&mut self, f: impl FnOnce(&mut Vec<u8>) -> R) -> R {
self.borrow_impl(f)
}
pub fn take(&mut self) -> Vec<u8> {
Cell::take(&self.0)
}
}
impl std::io::Write for RcWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.borrow(|v| v.extend_from_slice(buf));
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

View file

@ -1,566 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{memory::splat_mask, prelude::*};
use std::num::NonZeroUsize;
#[hdl]
pub struct ReadyValid<T> {
pub data: HdlOption<T>,
#[hdl(flip)]
pub ready: Bool,
}
impl<T: Type> ReadyValid<T> {
#[hdl]
pub fn firing(expr: Expr<Self>) -> Expr<Bool> {
#[hdl]
let firing: Bool = wire();
#[hdl]
match expr.data {
HdlNone => connect(firing, false),
HdlSome(_) => connect(firing, expr.ready),
}
firing
}
#[hdl]
pub fn firing_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> {
let expr = expr.to_expr();
let option_ty = Expr::ty(expr).data;
#[hdl]
let firing_data = wire(option_ty);
connect(firing_data, option_ty.HdlNone());
#[hdl]
if expr.ready {
connect(firing_data, expr.data);
}
firing_data
}
#[hdl]
pub fn map<R: Type>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<ReadyValid<R>> {
let data = HdlOption::map(expr.data, f);
#[hdl]
let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]);
connect(mapped.data, data);
connect(expr.ready, mapped.ready);
mapped
}
}
/// This debug port is only meant to assist the formal proof of the queue.
#[cfg(test)]
#[doc(hidden)]
#[hdl]
pub struct QueueDebugPort<Element, Index> {
#[hdl(flip)]
index_to_check: Index,
stored: Element,
inp_index: Index,
out_index: Index,
}
#[hdl_module]
pub fn queue<T: Type>(
ty: T,
capacity: NonZeroUsize,
inp_ready_is_comb: bool,
out_valid_is_comb: bool,
) {
let count_ty = UInt::range_inclusive(0..=capacity.get());
let index_ty = UInt::range(0..capacity.get());
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let inp: ReadyValid<T> = m.input(ReadyValid[ty]);
#[hdl]
let out: ReadyValid<T> = m.output(ReadyValid[ty]);
#[hdl]
let count: UInt = m.output(count_ty);
#[hdl]
let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
#[hdl]
let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
#[hdl]
let maybe_full_reg = reg_builder().clock_domain(cd).reset(false);
#[hdl]
let mut mem = memory(ty);
mem.depth(capacity.get());
let read_port = mem.new_read_port();
let write_port = mem.new_write_port();
#[hdl]
let inp_firing: Bool = wire();
connect(inp_firing, ReadyValid::firing(inp));
#[hdl]
let out_firing: Bool = wire();
connect(out_firing, ReadyValid::firing(out));
#[hdl]
let indexes_equal: Bool = wire();
connect(indexes_equal, inp_index_reg.cmp_eq(out_index_reg));
#[hdl]
let empty: Bool = wire();
connect(empty, indexes_equal & !maybe_full_reg);
#[hdl]
let full: Bool = wire();
connect(full, indexes_equal & maybe_full_reg);
connect(read_port.addr, out_index_reg);
connect(read_port.en, true);
connect(read_port.clk, cd.clk);
connect(write_port.addr, inp_index_reg);
connect(write_port.en, inp_firing);
connect(write_port.clk, cd.clk);
connect(write_port.data, HdlOption::unwrap_or(inp.data, ty.uninit()));
connect(write_port.mask, splat_mask(ty, true.to_expr()));
connect(inp.ready, !full);
if inp_ready_is_comb {
#[hdl]
if out.ready {
connect(inp.ready, true);
}
}
#[hdl]
if !empty {
connect(out.data, HdlSome(read_port.data));
} else {
if out_valid_is_comb {
connect(out.data, inp.data);
} else {
connect(out.data, HdlOption[ty].HdlNone());
}
}
#[hdl]
if inp_firing.cmp_ne(out_firing) {
connect(maybe_full_reg, inp_firing);
}
#[hdl]
if inp_firing {
#[hdl]
if inp_index_reg.cmp_eq(capacity.get() - 1) {
connect_any(inp_index_reg, 0_hdl_u0);
} else {
connect_any(inp_index_reg, inp_index_reg + 1_hdl_u1);
}
}
#[hdl]
if out_firing {
#[hdl]
if out_index_reg.cmp_eq(capacity.get() - 1) {
connect_any(out_index_reg, 0_hdl_u0);
} else {
connect_any(out_index_reg, out_index_reg + 1_hdl_u1);
}
}
#[hdl]
if indexes_equal {
#[hdl]
if maybe_full_reg {
connect_any(count, capacity);
} else {
connect_any(count, 0_hdl_u0);
}
} else {
if capacity.is_power_of_two() {
debug_assert_eq!(count_ty.width(), index_ty.width() + 1);
#[hdl]
let count_lower = wire(index_ty);
connect(
count_lower,
(inp_index_reg - out_index_reg).cast_to(index_ty),
); // wrap
connect(count, count_lower.cast_to(count_ty));
} else {
debug_assert_eq!(count_ty.width(), index_ty.width());
#[hdl]
if inp_index_reg.cmp_lt(out_index_reg) {
connect(count, inp_index_reg + capacity - out_index_reg);
} else {
connect(count, inp_index_reg - out_index_reg);
}
}
}
// These debug ports expose some internal state during the Induction phase
// of Formal Verification. They are not present in normal use.
#[cfg(test)]
{
#[hdl]
let dbg: QueueDebugPort<T, UInt> = m.output(QueueDebugPort[ty][index_ty]);
// read the memory word currently stored at some fixed index
let debug_port = mem.new_read_port();
connect(debug_port.addr, dbg.index_to_check);
connect(debug_port.en, true);
connect(debug_port.clk, cd.clk);
connect(dbg.stored, debug_port.data);
// also expose the current read and write indices
connect(dbg.inp_index, inp_index_reg);
connect(dbg.out_index, out_index_reg);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cli::FormalMode, firrtl::ExportOptions,
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
ty::StaticType,
};
use std::num::NonZero;
#[track_caller]
fn test_queue(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
assert_formal(
format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"),
queue_test(capacity, inp_ready_is_comb, out_valid_is_comb),
FormalMode::Prove,
2,
None,
ExportOptions {
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
..ExportOptions::default()
},
);
/// Formal verification of the FIFO queue
///
/// The strategy derives from the observation that, if we filter its
/// input and output streams to consider just one in every N reads and
/// writes (where N is the FIFO capacity), then the FIFO effectively
/// behaves as a one-entry FIFO.
///
/// In particular, any counterexample of the full FIFO behaving badly
/// will also be caught by one of the filtered versions (one which
/// happens to be in phase with the offending input or output).
#[hdl_module]
fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
#[hdl]
let clk: Clock = m.input();
#[hdl]
let cd = wire();
connect(
cd,
#[hdl]
ClockDomain {
clk,
rst: formal_reset().to_reset(),
},
);
// random input data
#[hdl]
let inp_data: HdlOption<UInt<8>> = wire();
#[hdl]
if any_seq(Bool) {
connect(inp_data, HdlSome(any_seq(UInt::<8>::TYPE)));
} else {
connect(inp_data, HdlNone());
}
// assert output ready at random
#[hdl]
let out_ready: Bool = wire();
connect(out_ready, any_seq(Bool));
// The current number of elements in the FIFO ranges from zero to
// maximum capacity, inclusive.
let count_ty = UInt::range_inclusive(0..=capacity.get());
// type for counters that wrap around at the FIFO capacity
let index_ty = UInt::range(0..capacity.get());
// among all entries of the FIFO internal circular memory, choose
// one at random to check
#[hdl]
let index_to_check = wire(index_ty);
connect(index_to_check, any_const(index_ty));
hdl_assume(clk, index_to_check.cmp_lt(capacity.get()), "");
// instantiate and connect the queue
#[hdl]
let dut = instance(queue(
UInt[ConstUsize::<8>],
capacity,
inp_ready_is_comb,
out_valid_is_comb,
));
connect(dut.cd, cd);
connect(dut.inp.data, inp_data);
connect(dut.out.ready, out_ready);
// Keep an independent count of words in the FIFO. Ensure that
// it's always correct, and never overflows.
#[hdl]
let expected_count_reg = reg_builder().clock_domain(cd).reset(count_ty.zero());
#[hdl]
if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) {
hdl_assert(clk, expected_count_reg.cmp_ne(capacity.get()), "");
connect_any(expected_count_reg, expected_count_reg + 1u8);
} else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) {
hdl_assert(clk, expected_count_reg.cmp_ne(count_ty.zero()), "");
connect_any(expected_count_reg, expected_count_reg - 1u8);
}
hdl_assert(clk, expected_count_reg.cmp_eq(dut.count), "");
// keep an independent write index into the FIFO's circular buffer
#[hdl]
let inp_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero());
#[hdl]
if ReadyValid::firing(dut.inp) {
#[hdl]
if inp_index_reg.cmp_ne(capacity.get() - 1) {
connect_any(inp_index_reg, inp_index_reg + 1u8);
} else {
connect_any(inp_index_reg, 0_hdl_u0);
}
}
// keep an independent read index into the FIFO's circular buffer
#[hdl]
let out_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero());
#[hdl]
if ReadyValid::firing(dut.out) {
#[hdl]
if out_index_reg.cmp_ne(capacity.get() - 1) {
connect_any(out_index_reg, out_index_reg + 1u8);
} else {
connect_any(out_index_reg, 0_hdl_u0);
}
}
// filter the input data stream, predicated by the read index
// matching the chosen position in the FIFO's circular buffer
#[hdl]
let inp_index_matches = wire();
connect(inp_index_matches, inp_index_reg.cmp_eq(index_to_check));
#[hdl]
let inp_firing_data = wire();
connect(inp_firing_data, HdlNone());
#[hdl]
if inp_index_matches {
connect(inp_firing_data, ReadyValid::firing_data(dut.inp));
}
// filter the output data stream, predicated by the write index
// matching the chosen position in the FIFO's circular buffer
#[hdl]
let out_index_matches = wire();
connect(out_index_matches, out_index_reg.cmp_eq(index_to_check));
#[hdl]
let out_firing_data = wire();
connect(out_firing_data, HdlNone());
#[hdl]
if out_index_matches {
connect(out_firing_data, ReadyValid::firing_data(dut.out));
}
// Implement a one-entry FIFO and ensure its equivalence to the
// filtered FIFO.
//
// the holding register for our one-entry FIFO
#[hdl]
let stored_reg = reg_builder().clock_domain(cd).reset(HdlNone());
#[hdl]
match stored_reg {
// If the holding register is empty...
HdlNone => {
#[hdl]
match inp_firing_data {
// ... and we are not receiving data, then we must not
// transmit any data.
HdlNone => hdl_assert(clk, HdlOption::is_none(out_firing_data), ""),
// If we are indeed receiving some data...
HdlSome(data_in) => {
#[hdl]
match out_firing_data {
// ... and transmitting at the same time, we
// must be transmitting the input data itself,
// since the holding register is empty.
HdlSome(data_out) => hdl_assert(clk, data_out.cmp_eq(data_in), ""),
// If we are receiving, but not transmitting,
// store the received data in the holding
// register.
HdlNone => connect(stored_reg, HdlSome(data_in)),
}
}
}
}
// If there is some value stored in the holding register...
HdlSome(stored) => {
#[hdl]
match out_firing_data {
// ... and we are not transmitting it, we cannot
// receive any more data.
HdlNone => hdl_assert(clk, HdlOption::is_none(inp_firing_data), ""),
// If we are transmitting a previously stored value...
HdlSome(data_out) => {
// ... it must be the same data we stored earlier.
hdl_assert(clk, data_out.cmp_eq(stored), "");
// Also, accept new data, if any. Otherwise,
// let the holding register become empty.
connect(stored_reg, inp_firing_data);
}
}
}
}
// from now on, some extra assertions in order to pass induction
// sync the holding register, when it's occupied, to the
// corresponding entry in the FIFO's circular buffer
connect(dut.dbg.index_to_check, index_to_check);
#[hdl]
if let HdlSome(stored) = stored_reg {
hdl_assert(clk, stored.cmp_eq(dut.dbg.stored), "");
}
// sync the read and write indices
hdl_assert(clk, inp_index_reg.cmp_eq(dut.dbg.inp_index), "");
hdl_assert(clk, out_index_reg.cmp_eq(dut.dbg.out_index), "");
// the indices should never go past the capacity, but induction
// doesn't know that...
hdl_assert(clk, inp_index_reg.cmp_lt(capacity.get()), "");
hdl_assert(clk, out_index_reg.cmp_lt(capacity.get()), "");
// strongly constrain the state of the holding register
//
// The holding register is full if and only if the corresponding
// FIFO entry was written to and not yet read. In other words, if
// the number of pending reads until the chosen entry is read out
// is greater than the current FIFO count, then the entry couldn't
// be in the FIFO in the first place.
#[hdl]
let pending_reads: UInt = wire(index_ty);
// take care of wrap-around when subtracting indices, add the
// capacity amount to keep the result positive if necessary
#[hdl]
if index_to_check.cmp_ge(out_index_reg) {
connect(pending_reads, index_to_check - out_index_reg);
} else {
connect(
pending_reads,
index_to_check + capacity.get() - out_index_reg,
);
}
// check whether the chosen entry is in the FIFO
#[hdl]
let expected_stored: Bool = wire();
connect(expected_stored, pending_reads.cmp_lt(dut.count));
// sync with the state of the holding register
hdl_assert(
clk,
expected_stored.cmp_eq(HdlOption::is_some(stored_reg)),
"",
);
}
}
#[test]
fn test_1_false_false() {
test_queue(NonZero::new(1).unwrap(), false, false);
}
#[test]
fn test_1_false_true() {
test_queue(NonZero::new(1).unwrap(), false, true);
}
#[test]
fn test_1_true_false() {
test_queue(NonZero::new(1).unwrap(), true, false);
}
#[test]
fn test_1_true_true() {
test_queue(NonZero::new(1).unwrap(), true, true);
}
#[test]
fn test_2_false_false() {
test_queue(NonZero::new(2).unwrap(), false, false);
}
#[test]
fn test_2_false_true() {
test_queue(NonZero::new(2).unwrap(), false, true);
}
#[test]
fn test_2_true_false() {
test_queue(NonZero::new(2).unwrap(), true, false);
}
#[test]
fn test_2_true_true() {
test_queue(NonZero::new(2).unwrap(), true, true);
}
#[test]
fn test_3_false_false() {
test_queue(NonZero::new(3).unwrap(), false, false);
}
#[test]
fn test_3_false_true() {
test_queue(NonZero::new(3).unwrap(), false, true);
}
#[test]
fn test_3_true_false() {
test_queue(NonZero::new(3).unwrap(), true, false);
}
#[test]
fn test_3_true_true() {
test_queue(NonZero::new(3).unwrap(), true, true);
}
#[test]
fn test_4_false_false() {
test_queue(NonZero::new(4).unwrap(), false, false);
}
#[test]
fn test_4_false_true() {
test_queue(NonZero::new(4).unwrap(), false, true);
}
#[test]
fn test_4_true_false() {
test_queue(NonZero::new(4).unwrap(), true, false);
}
#[test]
fn test_4_true_true() {
test_queue(NonZero::new(4).unwrap(), true, true);
}
#[test]
fn test_many_false_false() {
test_queue(NonZero::new((2 << 16) - 5).unwrap(), false, false);
}
#[test]
fn test_many_false_true() {
test_queue(NonZero::new((2 << 16) - 5).unwrap(), false, true);
}
#[test]
fn test_many_true_false() {
test_queue(NonZero::new((2 << 16) - 5).unwrap(), true, false);
}
#[test]
fn test_many_true_true() {
test_queue(NonZero::new((2 << 16) - 5).unwrap(), true, true);
}
}

View file

@ -1,5 +1,3 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
mod safety_boundary { mod safety_boundary {
use std::{cell::Cell, ptr::NonNull}; use std::{cell::Cell, ptr::NonNull};
@ -106,9 +104,3 @@ impl<T: ?Sized> ScopedRef<T> {
self.0.with_opt(f) self.0.with_opt(f)
} }
} }
impl<T: ?Sized> Default for ScopedRef<T> {
fn default() -> Self {
Self::new()
}
}

View file

@ -1,31 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::{
io::{self, BufRead},
str,
};
pub(crate) fn streaming_read_utf8<R: BufRead, E: From<io::Error>>(
reader: R,
mut callback: impl FnMut(&str) -> Result<(), E>,
) -> Result<(), E> {
let mut buf = [0; 4];
let mut buf_len = 0;
for byte in reader.bytes() {
buf[buf_len] = byte?;
buf_len += 1;
match str::from_utf8(&buf[..buf_len]) {
Ok(buf) => {
callback(buf)?;
buf_len = 0;
}
Err(e) => {
if e.error_len().is_some() {
callback("\u{FFFD}")?; // replacement character
buf_len = 0;
}
}
}
}
Ok(())
}

View file

@ -0,0 +1,88 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
int::{DynIntType, DynSIntType, DynUIntType, IntTypeTrait, SIntType},
ty::{Type, Value},
};
use std::ops::RangeBounds;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)]
pub struct Valueless<T> {
pub ty: T,
}
impl<T: Type> Valueless<T> {
pub fn to_canonical(&self) -> Valueless<T::CanonicalType> {
Valueless {
ty: self.ty.canonical(),
}
}
pub fn from_canonical(v: Valueless<T::CanonicalType>) -> Self {
Valueless {
ty: T::from_canonical_type(v.ty),
}
}
}
mod sealed {
pub trait Sealed {}
}
pub trait ValuelessTr: sealed::Sealed {
type Type: Type<Value = Self::Value>;
type Value: Value<Type = Self::Type>;
}
impl<T> sealed::Sealed for Valueless<T> {}
impl<T: Type> ValuelessTr for Valueless<T> {
type Type = T;
type Value = T::Value;
}
impl<T: IntTypeTrait> Valueless<T> {
pub fn signum(&self) -> Valueless<SIntType<2>> {
Valueless::default()
}
pub fn as_same_width_uint(self) -> Valueless<T::SameWidthUInt> {
Valueless {
ty: self.ty.as_same_width_uint(),
}
}
pub fn as_same_width_sint(self) -> Valueless<T::SameWidthSInt> {
Valueless {
ty: self.ty.as_same_width_sint(),
}
}
pub fn as_same_value_uint(self) -> Valueless<DynUIntType> {
Valueless {
ty: self.ty.as_same_value_uint(),
}
}
pub fn as_same_value_sint(self) -> Valueless<DynSIntType> {
Valueless {
ty: self.ty.as_same_value_sint(),
}
}
pub fn concat<HighType: IntTypeTrait>(
&self,
high_part: Valueless<HighType>,
) -> Valueless<DynIntType<HighType::Signed>> {
let ty = DynIntType::new(
self.ty
.width()
.checked_add(high_part.ty.width())
.expect("result has too many bits"),
);
Valueless { ty }
}
pub fn repeat(&self, count: usize) -> Valueless<DynIntType<T::Signed>> {
let width = self.ty.width();
let ty = DynIntType::new(width.checked_mul(count).expect("result has too many bits"));
Valueless { ty }
}
pub fn slice<I: RangeBounds<usize>>(&self, index: I) -> Valueless<DynUIntType> {
let ty = self.ty.slice(index);
Valueless { ty }
}
}

View file

@ -1,13 +1,13 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{Expr, Flow, ToExpr}, expr::Flow,
intern::Interned, intern::Interned,
module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire}, module::{NameId, ScopedNameId},
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
}; };
use std::{cell::RefCell, fmt, rc::Rc}; use std::fmt;
#[derive(Copy, Clone, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Wire<T: Type> { pub struct Wire<T: Type> {
@ -37,18 +37,6 @@ impl<T: Type> Wire<T> {
ty: ty.canonical(), ty: ty.canonical(),
} }
} }
pub fn from_canonical(v: Wire<CanonicalType>) -> Self {
let Wire {
name,
source_location,
ty,
} = v;
Self {
name,
source_location,
ty: T::from_canonical(ty),
}
}
pub fn ty(&self) -> T { pub fn ty(&self) -> T {
self.ty self.ty
} }
@ -88,57 +76,3 @@ impl<T: Type> Wire<T> {
true true
} }
} }
#[derive(Clone)]
pub struct IncompleteWire {
pub(crate) declaration: Rc<RefCell<IncompleteDeclaration>>,
}
impl IncompleteWire {
#[track_caller]
pub fn complete<T: Type>(&mut self, ty: T) -> Expr<T> {
let canonical_type = ty.canonical();
let mut declaration = self.declaration.borrow_mut();
if let IncompleteDeclaration::Incomplete {
name,
source_location,
} = *declaration
{
*declaration = IncompleteDeclaration::Complete(
StmtWire {
annotations: (),
wire: Wire {
name,
source_location,
ty: canonical_type,
},
}
.into(),
);
}
match *declaration {
IncompleteDeclaration::Complete(StmtDeclaration::Wire(StmtWire {
wire:
Wire {
name,
source_location,
ty: wire_ty,
},
..
})) => {
drop(declaration);
assert_eq!(wire_ty, canonical_type, "type mismatch");
Wire {
name,
source_location,
ty,
}
.to_expr()
}
IncompleteDeclaration::Taken => panic!("can't use wire outside of containing module"),
IncompleteDeclaration::Complete(_) | IncompleteDeclaration::Incomplete { .. } => {
unreachable!()
}
}
}
}

View file

@ -1,295 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! Formal tests in Fayalite
use fayalite::{
cli::FormalMode,
clock::{Clock, ClockDomain},
expr::{CastTo, HdlPartialEq},
firrtl::ExportOptions,
formal::{any_const, any_seq, formal_reset, hdl_assert, hdl_assume},
hdl, hdl_module,
int::{Bool, DynSize, Size, UInt, UIntType},
module::{connect, connect_any, instance, memory, reg_builder, wire},
reset::ToReset,
testing::assert_formal,
ty::StaticType,
};
/// Test hidden state
///
/// Hidden state can cause problems for induction, since the formal engine
/// can assign invalid values to the state registers, making it traverse
/// valid but unreachable states.
///
/// One solution is to go sufficiently in the past so the engine is forced
/// to eventually take a reachable state. This may be hampered by
/// existence of loops, then assumptions may be added to break them.
///
/// Another solution is to "open the black box" and add additional
/// assertions involving the hidden state, so that the unreachable states
/// become invalid as well.
///
/// Both approaches are taken here.
///
/// See [Claire Wolf's presentation] and [Zipcpu blog article].
///
/// [Claire Wolf's presentation]: https://web.archive.org/web/20200115081517fw_/http://www.clifford.at/papers/2017/smtbmc-sby/
/// [Zipcpu blog article]: https://zipcpu.com/blog/2018/03/10/induction-exercise.html
mod hidden_state {
use super::*;
/// Test hidden state by shift registers
///
/// The code implement the ideas from an article in the [Zipcpu blog]. Two
/// shift registers are fed from the same input, so they should always have
/// the same value. However the only observable is a comparison of their
/// last bit, all the others are hidden. To complicate matters, an enable
/// signal causes a loop in state space.
///
/// [Zipcpu blog]: https://zipcpu.com/blog/2018/03/10/induction-exercise.html
#[test]
fn shift_register() {
enum ConstraintMode {
WithExtraAssertions,
WithExtraAssumptions,
}
use ConstraintMode::*;
#[hdl_module]
fn test_module(constraint_mode: ConstraintMode) {
#[hdl]
let clk: Clock = m.input();
#[hdl]
let cd = wire();
connect(
cd,
#[hdl]
ClockDomain {
clk,
rst: formal_reset().to_reset(),
},
);
// input signal for the shift registers
#[hdl]
let i: Bool = wire();
connect(i, any_seq(Bool));
// shift enable signal
#[hdl]
let en: Bool = wire();
connect(en, any_seq(Bool));
// comparison output
#[hdl]
let o: Bool = wire();
// shift registers, with enable
#[hdl]
let r1 = reg_builder().clock_domain(cd).reset(0u8);
#[hdl]
let r2 = reg_builder().clock_domain(cd).reset(0u8);
#[hdl]
if en {
connect_any(r1, (r1 << 1) | i.cast_to(UInt[1]));
connect_any(r2, (r2 << 1) | i.cast_to(UInt[1]));
}
// compare last bits of both shift registers
connect(o, r1[7].cmp_eq(r2[7]));
// what we want to prove: last bits are always equal
hdl_assert(clk, o, "");
// additional terms below are only needed to assist with the induction proof
match constraint_mode {
WithExtraAssertions => {
// "Open the box": add assertions about hidden state.
// In this case, the hidden bits are also always equal.
hdl_assert(clk, r1.cmp_eq(r2), "");
}
WithExtraAssumptions => {
// Break the loop, do not allow "en" to remain low forever
#[hdl]
let past_en_reg = reg_builder().clock_domain(cd).reset(false);
connect(past_en_reg, en);
hdl_assume(clk, past_en_reg | en, "");
}
}
}
// we need a minimum of 16 steps so we can constrain all eight shift register bits,
// given that we are allowed to disable the shift once every two cycles.
assert_formal(
"shift_register_with_assumptions",
test_module(WithExtraAssumptions),
FormalMode::Prove,
16,
None,
ExportOptions::default(),
);
// here a couple of cycles is enough
assert_formal(
"shift_register_with_assertions",
test_module(WithExtraAssertions),
FormalMode::Prove,
2,
None,
ExportOptions::default(),
);
}
}
/// Formal verification of designs containing memories
///
/// There is a trick for memories, described in the [Zipcpu blog].
/// First, select a fixed but arbitrary memory address, monitoring all reads
/// and writes made to it. Then, assert that anything read from that location
/// matches the last stored value.
///
/// A difficulty for induction is that the memory represents [hidden_state]. A
/// solution is to include an additional read port to the memory and assert
/// that the memory location effectively contains the last stored value.
/// This additional debug port is present only to assist the proof and is
/// unused (optimized out) in actual use.
///
/// [Zipcpu blog]: <https://zipcpu.com/zipcpu/2018/07/13/memories.html>
mod memory {
use super::*;
/// Test a simple 8-bit SRAM model
#[test]
fn test_sram() {
#[hdl]
struct WritePort<AddrWidth: Size> {
addr: UIntType<AddrWidth>,
data: UInt<8>,
en: Bool,
}
#[hdl]
struct ReadPort<AddrWidth: Size> {
addr: UIntType<AddrWidth>,
#[hdl(flip)]
data: UInt<8>,
}
/// This debug port is only meant to assist the proof.
/// For normal use in a design, a wrapper could be provided,
/// omitting this port.
/// The implementation is forbidden to use any information
/// provided on this port in its internal workings.
#[hdl]
struct DebugPort<AddrWidth: Size> {
selected: UIntType<AddrWidth>,
stored: UInt<8>,
wrote: Bool,
}
/// simple 1R1W SRAM model (one asynchronous read port and one
/// independent write port) with `n`-bit address width
#[hdl_module]
fn example_sram(n: usize) {
#[hdl]
let wr: WritePort<DynSize> = m.input(WritePort[n]);
#[hdl]
let rd: ReadPort<DynSize> = m.input(ReadPort[n]);
#[hdl]
let cd: ClockDomain = m.input();
// declare and connect the backing memory
#[hdl]
let mut mem = memory();
mem.depth(1 << n);
let read_port = mem.new_read_port();
let write_port = mem.new_write_port();
connect(write_port.clk, cd.clk);
connect(write_port.addr, wr.addr);
connect(write_port.en, wr.en);
connect(write_port.data, wr.data);
connect(write_port.mask, true);
connect(read_port.clk, cd.clk);
connect(read_port.addr, rd.addr);
connect(read_port.en, true);
connect(rd.data, read_port.data);
// To assist with induction, ensure that the chosen memory location
// really contains, always, the last value written to it.
#[hdl]
let dbg: DebugPort<DynSize> = m.input(DebugPort[n]);
let debug_port = mem.new_read_port();
connect(debug_port.en, true);
connect(debug_port.clk, cd.clk);
connect(debug_port.addr, dbg.selected);
#[hdl]
if dbg.wrote {
hdl_assert(cd.clk, debug_port.data.cmp_eq(dbg.stored), "");
// Try commenting out the assert above, induction will fail.
// Opening the trace, it can be seen that the memory contents
// and the stored value don't match, which is an unreachable
// state. By asserting the above, it will become invalid
// as well, so induction will skip this kind of situation.
}
}
/// formal verification of the SRAM module, parametrized by the
/// address bit-width
#[hdl_module]
fn test_module(n: usize) {
#[hdl]
let clk: Clock = m.input();
let cd = #[hdl]
ClockDomain {
clk,
rst: formal_reset().to_reset(),
};
// instantiate the SRAM model, connecting its inputs to
// a random sequence
#[hdl]
let rd: ReadPort<DynSize> = wire(ReadPort[n]);
connect(rd.addr, any_seq(UInt[n]));
#[hdl]
let wr: WritePort<DynSize> = wire(WritePort[n]);
connect(wr.addr, any_seq(UInt[n]));
connect(wr.data, any_seq(UInt::<8>::TYPE));
connect(wr.en, any_seq(Bool));
#[hdl]
let dut = instance(example_sram(n));
connect(dut.cd, cd);
connect(dut.rd, rd);
connect(dut.wr, wr);
// select a fixed but arbitrary test address
#[hdl]
let selected = wire(UInt[n]);
connect(selected, any_const(UInt[n]));
// store the last value written to that address
#[hdl]
let stored: UInt<8> = reg_builder().clock_domain(cd).reset(0u8);
// since memories are not initialized, track whether we wrote to the
// memory at least once
#[hdl]
let wrote: Bool = reg_builder().clock_domain(cd).reset(false);
// on a write, capture the last written value
#[hdl]
if wr.en & wr.addr.cmp_eq(selected) {
connect(stored, wr.data);
connect(wrote, true);
}
// on a read, assert that the read value is the same which was stored
#[hdl]
if rd.addr.cmp_eq(selected) & wrote {
hdl_assert(clk, rd.data.cmp_eq(stored), "");
}
// to assist induction, pass our state to the underlying instance
let dbg = #[hdl]
DebugPort {
selected,
stored,
wrote,
};
connect(dut.dbg, dbg);
}
assert_formal(
"sram",
test_module(8),
FormalMode::Prove,
2,
None,
ExportOptions::default(),
);
}
}

View file

@ -1,13 +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
use fayalite::{ use fayalite::prelude::*;
bundle::BundleType,
enum_::EnumType,
int::{BoolOrIntType, IntType},
prelude::*,
ty::StaticType,
};
use std::marker::PhantomData;
#[hdl(outline_generated)] #[hdl(outline_generated)]
pub struct S<T, Len: Size, T2> { pub struct S<T, Len: Size, T2> {
@ -15,7 +8,6 @@ pub struct S<T, Len: Size, T2> {
b: UInt<3>, b: UInt<3>,
pub(crate) c: ArrayType<UInt<1>, Len>, pub(crate) c: ArrayType<UInt<1>, Len>,
pub d: T2, pub d: T2,
pub _phantom: PhantomData<(T, Len)>,
} }
#[hdl(outline_generated)] #[hdl(outline_generated)]
@ -31,163 +23,9 @@ pub enum E<T> {
A, A,
B(UInt<3>), B(UInt<3>),
C(T), C(T),
D(TyAlias2),
E(TyAlias<Bool, ConstUsize<1>, { 1 + 2 }>),
} }
#[hdl(outline_generated)] #[hdl(outline_generated)]
pub struct S2<T = ()> { pub struct S2<T = ()> {
pub v: E<T>, pub v: E<T>,
} }
#[hdl(outline_generated)]
pub type TyAlias<T, Sz: Size, const C: usize, D = ()> = Array<S<T, Sz, D>, C>;
#[hdl(outline_generated)]
pub type TyAlias2 = TyAlias<UInt<8>, ConstUsize<24>, 5>;
// check that #[hdl] properly handles hygiene
macro_rules! types_in_macros {
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident) => {
#[hdl]
struct $F {}
#[hdl]
struct $A<$B, $C: Size, const $D: usize, $E = $F> {
$a: $B,
$b: UIntType<$C>,
$c: SInt<$D>,
$d: HdlOption<$E>,
$e: $E,
$f: $F,
}
#[allow(non_camel_case_types)]
#[hdl]
enum $B<$C: Size, const $D: usize, $E = $F> {
$a($A<(), $C, $D, $E>),
$b(UIntType<$C>),
$c(SInt<$D>),
$d,
$e($E),
$f($F),
}
};
// ensure every identifier has different hygiene
() => {
types_in_macros!(a);
};
($a:ident) => {
types_in_macros!($a, b);
};
($a:ident, $b:ident) => {
types_in_macros!($a, $b, c);
};
($a:ident, $b:ident, $c:ident) => {
types_in_macros!($a, $b, $c, d);
};
($a:ident, $b:ident, $c:ident, $d:ident) => {
types_in_macros!($a, $b, $c, $d, e);
};
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident) => {
types_in_macros!($a, $b, $c, $d, $e, f);
};
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident) => {
types_in_macros!($a, $b, $c, $d, $e, $f, A);
};
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident) => {
types_in_macros!($a, $b, $c, $d, $e, $f, $A, B);
};
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident) => {
types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, C);
};
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident) => {
types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, D);
};
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident) => {
types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, $D, E);
};
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident) => {
types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, $D, $E, F);
};
}
types_in_macros!();
mod bound_kind {
use fayalite::prelude::*;
#[hdl]
pub struct Type<T> {
v: T,
}
#[hdl]
pub struct Size<T: ::fayalite::int::Size> {
v: UIntType<T>,
}
}
macro_rules! check_bounds {
($name:ident<$(#[$field:ident, $kind:ident] $var:ident: $($bound:ident +)*),*>) => {
#[hdl(outline_generated)]
struct $name<$($var: $($bound +)*,)*> {
$($field: bound_kind::$kind<$var>,)*
}
};
}
check_bounds!(CheckBoundsS0<#[a, Size] A: Size +>);
check_bounds!(CheckBoundsS1<#[a, Size] A: KnownSize +>);
check_bounds!(CheckBoundsT0<#[a, Type] A: Type +>);
check_bounds!(CheckBoundsT1<#[a, Type] A: BoolOrIntType +>);
check_bounds!(CheckBoundsT2<#[a, Type] A: BundleType +>);
check_bounds!(CheckBoundsT3<#[a, Type] A: EnumType +>);
check_bounds!(CheckBoundsT4<#[a, Type] A: IntType +>);
check_bounds!(CheckBoundsT5<#[a, Type] A: StaticType +>);
check_bounds!(CheckBoundsSS0<#[a, Size] A: Size +, #[b, Size] B: Size +>);
check_bounds!(CheckBoundsSS1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +>);
check_bounds!(CheckBoundsST0<#[a, Size] A: Size +, #[b, Type] B: Type +>);
check_bounds!(CheckBoundsST1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +>);
check_bounds!(CheckBoundsTS0<#[a, Type] A: Type +, #[b, Size] B: Size +>);
check_bounds!(CheckBoundsTS1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +>);
check_bounds!(CheckBoundsTS2<#[a, Type] A: BundleType +, #[b, Size] B: Size +>);
check_bounds!(CheckBoundsTS3<#[a, Type] A: EnumType +, #[b, Size] B: Size +>);
check_bounds!(CheckBoundsTS4<#[a, Type] A: IntType +, #[b, Size] B: Size +>);
check_bounds!(CheckBoundsTS5<#[a, Type] A: StaticType +, #[b, Size] B: Size +>);
check_bounds!(CheckBoundsTT0<#[a, Type] A: Type +, #[b, Type] B: Type +>);
check_bounds!(CheckBoundsTT1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +>);
check_bounds!(CheckBoundsTT2<#[a, Type] A: BundleType +, #[b, Type] B: Type +>);
check_bounds!(CheckBoundsTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +>);
check_bounds!(CheckBoundsTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +>);
check_bounds!(CheckBoundsTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +>);
check_bounds!(CheckBoundsSSS0<#[a, Size] A: Size +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsSSS1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsSST0<#[a, Size] A: Size +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsSST1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsSTS0<#[a, Size] A: Size +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsSTS1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsSTT0<#[a, Size] A: Size +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsSTT1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTSS0<#[a, Type] A: Type +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTSS1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTSS2<#[a, Type] A: BundleType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTSS3<#[a, Type] A: EnumType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTSS4<#[a, Type] A: IntType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTSS5<#[a, Type] A: StaticType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTST0<#[a, Type] A: Type +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTST1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTST2<#[a, Type] A: BundleType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTST3<#[a, Type] A: EnumType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTST4<#[a, Type] A: IntType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTST5<#[a, Type] A: StaticType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTTS0<#[a, Type] A: Type +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTTS1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTTS2<#[a, Type] A: BundleType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTTS3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTTS4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTTS5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
check_bounds!(CheckBoundsTTT0<#[a, Type] A: Type +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTTT1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
check_bounds!(CheckBoundsTTT2<#[a, Type] A: BundleType +, #[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!(CheckBoundsTTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,283 +0,0 @@
$timescale 1 ps $end
$scope module array_rw $end
$scope struct array_in $end
$var wire 8 ! \[0] $end
$var wire 8 " \[1] $end
$var wire 8 # \[2] $end
$var wire 8 $ \[3] $end
$var wire 8 % \[4] $end
$var wire 8 & \[5] $end
$var wire 8 ' \[6] $end
$var wire 8 ( \[7] $end
$var wire 8 ) \[8] $end
$var wire 8 * \[9] $end
$var wire 8 + \[10] $end
$var wire 8 , \[11] $end
$var wire 8 - \[12] $end
$var wire 8 . \[13] $end
$var wire 8 / \[14] $end
$var wire 8 0 \[15] $end
$upscope $end
$scope struct array_out $end
$var wire 8 1 \[0] $end
$var wire 8 2 \[1] $end
$var wire 8 3 \[2] $end
$var wire 8 4 \[3] $end
$var wire 8 5 \[4] $end
$var wire 8 6 \[5] $end
$var wire 8 7 \[6] $end
$var wire 8 8 \[7] $end
$var wire 8 9 \[8] $end
$var wire 8 : \[9] $end
$var wire 8 ; \[10] $end
$var wire 8 < \[11] $end
$var wire 8 = \[12] $end
$var wire 8 > \[13] $end
$var wire 8 ? \[14] $end
$var wire 8 @ \[15] $end
$upscope $end
$var wire 8 A read_index $end
$var wire 8 B read_data $end
$var wire 8 C write_index $end
$var wire 8 D write_data $end
$var wire 1 E write_en $end
$scope struct array_wire $end
$var wire 8 F \[0] $end
$var wire 8 G \[1] $end
$var wire 8 H \[2] $end
$var wire 8 I \[3] $end
$var wire 8 J \[4] $end
$var wire 8 K \[5] $end
$var wire 8 L \[6] $end
$var wire 8 M \[7] $end
$var wire 8 N \[8] $end
$var wire 8 O \[9] $end
$var wire 8 P \[10] $end
$var wire 8 Q \[11] $end
$var wire 8 R \[12] $end
$var wire 8 S \[13] $end
$var wire 8 T \[14] $end
$var wire 8 U \[15] $end
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
b11111111 !
b1111111 "
b111111 #
b11111 $
b1111 %
b111 &
b11 '
b1 (
b0 )
b10000000 *
b11000000 +
b11100000 ,
b11110000 -
b11111000 .
b11111100 /
b11111110 0
b11111111 1
b1111111 2
b111111 3
b11111 4
b1111 5
b111 6
b11 7
b1 8
b0 9
b10000000 :
b11000000 ;
b11100000 <
b11110000 =
b11111000 >
b11111100 ?
b11111110 @
b0 A
b11111111 B
b0 C
b0 D
0E
b11111111 F
b1111111 G
b111111 H
b11111 I
b1111 J
b111 K
b11 L
b1 M
b0 N
b10000000 O
b11000000 P
b11100000 Q
b11110000 R
b11111000 S
b11111100 T
b11111110 U
$end
#1000000
b1 A
b1111111 B
#2000000
b10 A
b111111 B
#3000000
b11 A
b11111 B
#4000000
b100 A
b1111 B
#5000000
b101 A
b111 B
#6000000
b110 A
b11 B
#7000000
b111 A
b1 B
#8000000
b1000 A
b0 B
#9000000
b1001 A
b10000000 B
#10000000
b1010 A
b11000000 B
#11000000
b1011 A
b11100000 B
#12000000
b1100 A
b11110000 B
#13000000
b1101 A
b11111000 B
#14000000
b1110 A
b11111100 B
#15000000
b1111 A
b11111110 B
#16000000
b10000 A
b0 B
#17000000
b0 1
b0 A
1E
b0 F
#18000000
b11111111 1
b1 2
b11111111 B
b1 C
b1 D
b11111111 F
b1 G
#19000000
b1111111 2
b100 3
b10 C
b100 D
b1111111 G
b100 H
#20000000
b111111 3
b1001 4
b11 C
b1001 D
b111111 H
b1001 I
#21000000
b11111 4
b10000 5
b100 C
b10000 D
b11111 I
b10000 J
#22000000
b1111 5
b11001 6
b101 C
b11001 D
b1111 J
b11001 K
#23000000
b111 6
b100100 7
b110 C
b100100 D
b111 K
b100100 L
#24000000
b11 7
b110001 8
b111 C
b110001 D
b11 L
b110001 M
#25000000
b1 8
b1000000 9
b1000 C
b1000000 D
b1 M
b1000000 N
#26000000
b0 9
b1010001 :
b1001 C
b1010001 D
b0 N
b1010001 O
#27000000
b10000000 :
b1100100 ;
b1010 C
b1100100 D
b10000000 O
b1100100 P
#28000000
b11000000 ;
b1111001 <
b1011 C
b1111001 D
b11000000 P
b1111001 Q
#29000000
b11100000 <
b10010000 =
b1100 C
b10010000 D
b11100000 Q
b10010000 R
#30000000
b11110000 =
b10101001 >
b1101 C
b10101001 D
b11110000 R
b10101001 S
#31000000
b11111000 >
b11000100 ?
b1110 C
b11000100 D
b11111000 S
b11000100 T
#32000000
b11111100 ?
b11100001 @
b1111 C
b11100001 D
b11111100 T
b11100001 U
#33000000
b11111110 @
b10000 C
b0 D
b11111110 U
#34000000

View file

@ -1,189 +0,0 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 4,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i",
ty: Bool,
},
SlotDebugData {
name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(3), // (0x0) SlotDebugData { name: "", ty: Bool },
value: 0x0,
},
1: Const {
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
value: 0x1,
},
// at: module-XXXXXXXXXX.rs:4:1
2: Copy {
dest: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool },
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:5:1
3: BranchIfZero {
target: 5,
value: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:6:1
4: Copy {
dest: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool },
src: StatePartIndex<BigSlots>(3), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:1:1
5: Return,
],
..
},
pc: 5,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [],
},
big_slots: StatePart {
value: [
1,
0,
1,
0,
],
},
},
io: Instance {
name: <simulator>::conditional_assignment_last,
instantiated: Module {
name: conditional_assignment_last,
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::conditional_assignment_last,
instantiated: Module {
name: conditional_assignment_last,
..
},
}.i: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
},
made_initial_step: true,
needs_settle: false,
trace_decls: TraceModule {
name: "conditional_assignment_last",
children: [
TraceModuleIO {
name: "i",
child: TraceBool {
location: TraceScalarId(0),
name: "i",
flow: Source,
},
ty: Bool,
flow: Source,
},
TraceWire {
name: "w",
child: TraceBool {
location: TraceScalarId(1),
name: "w",
flow: Duplex,
},
ty: Bool,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigBool {
index: StatePartIndex<BigSlots>(0),
},
state: 0x1,
last_state: 0x0,
},
SimTrace {
id: TraceScalarId(1),
kind: BigBool {
index: StatePartIndex<BigSlots>(1),
},
state: 0x0,
last_state: 0x1,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 2 μs,
clocks_triggered: [],
..
}

View file

@ -1,14 +0,0 @@
$timescale 1 ps $end
$scope module conditional_assignment_last $end
$var wire 1 ! i $end
$var wire 1 " w $end
$upscope $end
$enddefinitions $end
$dumpvars
0!
1"
$end
#1000000
1!
0"
#2000000

View file

@ -1,142 +0,0 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 2,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(connect_const: connect_const).connect_const::o",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> },
value: 0x5,
},
// at: module-XXXXXXXXXX.rs:3:1
1: Copy {
dest: StatePartIndex<BigSlots>(0), // (0x5) SlotDebugData { name: "InstantiatedModule(connect_const: connect_const).connect_const::o", ty: UInt<8> },
src: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:1:1
2: Return,
],
..
},
pc: 2,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [],
},
big_slots: StatePart {
value: [
5,
5,
],
},
},
io: Instance {
name: <simulator>::connect_const,
instantiated: Module {
name: connect_const,
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::connect_const,
instantiated: Module {
name: connect_const,
..
},
}.o: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<8>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(connect_const: connect_const).connect_const::o",
ty: UInt<8>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
},
made_initial_step: true,
needs_settle: false,
trace_decls: TraceModule {
name: "connect_const",
children: [
TraceModuleIO {
name: "o",
child: TraceUInt {
location: TraceScalarId(0),
name: "o",
ty: UInt<8>,
flow: Sink,
},
ty: UInt<8>,
flow: Sink,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigUInt {
index: StatePartIndex<BigSlots>(0),
ty: UInt<8>,
},
state: 0x05,
last_state: 0x05,
},
],
trace_memories: {},
trace_writers: [],
instant: 0 s,
clocks_triggered: [],
..
}

View file

@ -1,229 +0,0 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 5,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out",
ty: AsyncReset,
},
SlotDebugData {
name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::bit_out",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: AsyncReset,
},
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
value: 0x1,
},
1: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset },
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:4:1
2: Copy {
dest: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out", ty: AsyncReset },
src: StatePartIndex<BigSlots>(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset },
},
// at: module-XXXXXXXXXX.rs:1:1
3: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x1) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out", ty: AsyncReset },
},
// at: module-XXXXXXXXXX.rs:5:1
4: Copy {
dest: StatePartIndex<BigSlots>(1), // (0x1) SlotDebugData { name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::bit_out", ty: Bool },
src: StatePartIndex<BigSlots>(4), // (0x1) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:1:1
5: Return,
],
..
},
pc: 5,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [],
},
big_slots: StatePart {
value: [
1,
1,
1,
1,
1,
],
},
},
io: Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
}.bit_out: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::bit_out",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
}.reset_out: CompiledValue {
layout: CompiledTypeLayout {
ty: AsyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out",
ty: AsyncReset,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
},
made_initial_step: true,
needs_settle: false,
trace_decls: TraceModule {
name: "connect_const_reset",
children: [
TraceModuleIO {
name: "reset_out",
child: TraceAsyncReset {
location: TraceScalarId(0),
name: "reset_out",
flow: Sink,
},
ty: AsyncReset,
flow: Sink,
},
TraceModuleIO {
name: "bit_out",
child: TraceBool {
location: TraceScalarId(1),
name: "bit_out",
flow: Sink,
},
ty: Bool,
flow: Sink,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigAsyncReset {
index: StatePartIndex<BigSlots>(0),
},
state: 0x1,
last_state: 0x1,
},
SimTrace {
id: TraceScalarId(1),
kind: BigBool {
index: StatePartIndex<BigSlots>(1),
},
state: 0x1,
last_state: 0x1,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 1 μs,
clocks_triggered: [],
..
}

View file

@ -1,11 +0,0 @@
$timescale 1 ps $end
$scope module connect_const_reset $end
$var wire 1 ! reset_out $end
$var wire 1 " bit_out $end
$upscope $end
$enddefinitions $end
$dumpvars
1!
1"
$end
#1000000

View file

@ -1,522 +0,0 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 4,
debug_data: [
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 10,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.rst",
ty: AsyncReset,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count",
ty: UInt<4>,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count_reg",
ty: UInt<4>,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count_reg$next",
ty: UInt<4>,
},
SlotDebugData {
name: "",
ty: UInt<4>,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: UInt<1>,
},
SlotDebugData {
name: "",
ty: UInt<5>,
},
SlotDebugData {
name: "",
ty: UInt<4>,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(7), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
value: 0x1,
},
1: Copy {
dest: StatePartIndex<BigSlots>(6), // (0x0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.rst", ty: AsyncReset },
},
// at: module-XXXXXXXXXX.rs:3:1
2: IsNonZeroDestIsSmall {
dest: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.rst", ty: AsyncReset },
},
// at: module-XXXXXXXXXX.rs:1:1
3: Const {
dest: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
value: 0x3,
},
// at: module-XXXXXXXXXX.rs:3:1
4: BranchIfZero {
target: 6,
value: StatePartIndex<BigSlots>(6), // (0x0) SlotDebugData { name: "", ty: Bool },
},
5: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:1:1
6: Add {
dest: StatePartIndex<BigSlots>(8), // (0x4) SlotDebugData { name: "", ty: UInt<5> },
lhs: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
rhs: StatePartIndex<BigSlots>(7), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
},
7: CastToUInt {
dest: StatePartIndex<BigSlots>(9), // (0x4) SlotDebugData { name: "", ty: UInt<4> },
src: StatePartIndex<BigSlots>(8), // (0x4) SlotDebugData { name: "", ty: UInt<5> },
dest_width: 4,
},
// at: module-XXXXXXXXXX.rs:4:1
8: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
src: StatePartIndex<BigSlots>(9), // (0x4) SlotDebugData { name: "", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:6:1
9: Copy {
dest: StatePartIndex<BigSlots>(2), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count", ty: UInt<4> },
src: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:3:1
10: IsNonZeroDestIsSmall {
dest: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.clk", ty: Clock },
},
11: AndSmall {
dest: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
rhs: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
12: BranchIfSmallNonZero {
target: 16,
value: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
13: BranchIfSmallZero {
target: 17,
value: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
14: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
},
15: Branch {
target: 17,
},
16: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
},
17: XorSmallImmediate {
dest: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:1:1
18: Return,
],
..
},
pc: 18,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [
0,
0,
1,
0,
],
},
big_slots: StatePart {
value: [
1,
0,
3,
3,
4,
3,
0,
1,
4,
4,
],
},
},
io: Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd: CompiledValue {
layout: CompiledTypeLayout {
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: AsyncReset,
},
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 2,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.rst",
ty: AsyncReset,
},
],
..
},
},
body: Bundle {
fields: [
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(0),
},
ty: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(1),
},
ty: CompiledTypeLayout {
ty: AsyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: AsyncReset,
},
],
..
},
},
body: Scalar,
},
},
],
},
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.clk: CompiledValue {
layout: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.rst: CompiledValue {
layout: CompiledTypeLayout {
ty: AsyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: AsyncReset,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.count: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<4>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count",
ty: UInt<4>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 },
},
write: None,
},
},
made_initial_step: true,
needs_settle: false,
trace_decls: TraceModule {
name: "counter",
children: [
TraceModuleIO {
name: "cd",
child: TraceBundle {
name: "cd",
fields: [
TraceClock {
location: TraceScalarId(0),
name: "clk",
flow: Source,
},
TraceAsyncReset {
location: TraceScalarId(1),
name: "rst",
flow: Source,
},
],
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: AsyncReset,
},
flow: Source,
},
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: AsyncReset,
},
flow: Source,
},
TraceModuleIO {
name: "count",
child: TraceUInt {
location: TraceScalarId(2),
name: "count",
ty: UInt<4>,
flow: Sink,
},
ty: UInt<4>,
flow: Sink,
},
TraceReg {
name: "count_reg",
child: TraceUInt {
location: TraceScalarId(3),
name: "count_reg",
ty: UInt<4>,
flow: Duplex,
},
ty: UInt<4>,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigClock {
index: StatePartIndex<BigSlots>(0),
},
state: 0x1,
last_state: 0x1,
},
SimTrace {
id: TraceScalarId(1),
kind: BigAsyncReset {
index: StatePartIndex<BigSlots>(1),
},
state: 0x0,
last_state: 0x0,
},
SimTrace {
id: TraceScalarId(2),
kind: BigUInt {
index: StatePartIndex<BigSlots>(2),
ty: UInt<4>,
},
state: 0x3,
last_state: 0x2,
},
SimTrace {
id: TraceScalarId(3),
kind: BigUInt {
index: StatePartIndex<BigSlots>(3),
ty: UInt<4>,
},
state: 0x3,
last_state: 0x3,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 66 μs,
clocks_triggered: [
StatePartIndex<SmallSlots>(1),
],
..
}

View file

@ -1,217 +0,0 @@
$timescale 1 ps $end
$scope module counter $end
$scope struct cd $end
$var wire 1 ! clk $end
$var wire 1 " rst $end
$upscope $end
$var wire 4 # count $end
$var reg 4 $ count_reg $end
$upscope $end
$enddefinitions $end
$dumpvars
0!
0"
b0 #
b0 $
$end
#500000
1"
b11 #
b11 $
#1000000
1!
#1500000
0"
#2000000
0!
#3000000
1!
b100 $
b100 #
#4000000
0!
#5000000
1!
b101 $
b101 #
#6000000
0!
#7000000
1!
b110 $
b110 #
#8000000
0!
#9000000
1!
b111 $
b111 #
#10000000
0!
#11000000
1!
b1000 $
b1000 #
#12000000
0!
#13000000
1!
b1001 $
b1001 #
#14000000
0!
#15000000
1!
b1010 $
b1010 #
#16000000
0!
#17000000
1!
b1011 $
b1011 #
#18000000
0!
#19000000
1!
b1100 $
b1100 #
#20000000
0!
#21000000
1!
b1101 $
b1101 #
#22000000
0!
#23000000
1!
b1110 $
b1110 #
#24000000
0!
#25000000
1!
b1111 $
b1111 #
#26000000
0!
#27000000
1!
b0 $
b0 #
#28000000
0!
#29000000
1!
b1 $
b1 #
#30000000
0!
#31000000
1!
b10 $
b10 #
#32000000
0!
#33000000
1!
b11 $
b11 #
#34000000
0!
#35000000
1!
b100 $
b100 #
#36000000
0!
#37000000
1!
b101 $
b101 #
#38000000
0!
#39000000
1!
b110 $
b110 #
#40000000
0!
#41000000
1!
b111 $
b111 #
#42000000
0!
#43000000
1!
b1000 $
b1000 #
#44000000
0!
#45000000
1!
b1001 $
b1001 #
#46000000
0!
#47000000
1!
b1010 $
b1010 #
#48000000
0!
#49000000
1!
b1011 $
b1011 #
#50000000
0!
#51000000
1!
b1100 $
b1100 #
#52000000
0!
#53000000
1!
b1101 $
b1101 #
#54000000
0!
#55000000
1!
b1110 $
b1110 #
#56000000
0!
#57000000
1!
b1111 $
b1111 #
#58000000
0!
#59000000
1!
b0 $
b0 #
#60000000
0!
#61000000
1!
b1 $
b1 #
#62000000
0!
#63000000
1!
b10 $
b10 #
#64000000
0!
#65000000
1!
b11 $
b11 #
#66000000

View file

@ -1,503 +0,0 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 4,
debug_data: [
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 9,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.rst",
ty: SyncReset,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count",
ty: UInt<4>,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count_reg",
ty: UInt<4>,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count_reg$next",
ty: UInt<4>,
},
SlotDebugData {
name: "",
ty: UInt<4>,
},
SlotDebugData {
name: "",
ty: UInt<1>,
},
SlotDebugData {
name: "",
ty: UInt<5>,
},
SlotDebugData {
name: "",
ty: UInt<4>,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:6:1
0: Copy {
dest: StatePartIndex<BigSlots>(2), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count", ty: UInt<4> },
src: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:1:1
1: Const {
dest: StatePartIndex<BigSlots>(6), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
value: 0x1,
},
2: Add {
dest: StatePartIndex<BigSlots>(7), // (0x4) SlotDebugData { name: "", ty: UInt<5> },
lhs: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
rhs: StatePartIndex<BigSlots>(6), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
},
3: CastToUInt {
dest: StatePartIndex<BigSlots>(8), // (0x4) SlotDebugData { name: "", ty: UInt<4> },
src: StatePartIndex<BigSlots>(7), // (0x4) SlotDebugData { name: "", ty: UInt<5> },
dest_width: 4,
},
// at: module-XXXXXXXXXX.rs:4:1
4: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
src: StatePartIndex<BigSlots>(8), // (0x4) SlotDebugData { name: "", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:3:1
5: IsNonZeroDestIsSmall {
dest: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.rst", ty: SyncReset },
},
// at: module-XXXXXXXXXX.rs:1:1
6: Const {
dest: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
value: 0x3,
},
// at: module-XXXXXXXXXX.rs:3:1
7: IsNonZeroDestIsSmall {
dest: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.clk", ty: Clock },
},
8: AndSmall {
dest: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
rhs: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
9: BranchIfSmallZero {
target: 14,
value: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
10: BranchIfSmallNonZero {
target: 13,
value: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
11: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
},
12: Branch {
target: 14,
},
13: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
},
14: XorSmallImmediate {
dest: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:1:1
15: Return,
],
..
},
pc: 15,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [
0,
0,
1,
0,
],
},
big_slots: StatePart {
value: [
1,
0,
3,
3,
4,
3,
1,
4,
4,
],
},
},
io: Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd: CompiledValue {
layout: CompiledTypeLayout {
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: SyncReset,
},
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 2,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.rst",
ty: SyncReset,
},
],
..
},
},
body: Bundle {
fields: [
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(0),
},
ty: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(1),
},
ty: CompiledTypeLayout {
ty: SyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SyncReset,
},
],
..
},
},
body: Scalar,
},
},
],
},
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.clk: CompiledValue {
layout: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.rst: CompiledValue {
layout: CompiledTypeLayout {
ty: SyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SyncReset,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.count: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<4>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count",
ty: UInt<4>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 },
},
write: None,
},
},
made_initial_step: true,
needs_settle: false,
trace_decls: TraceModule {
name: "counter",
children: [
TraceModuleIO {
name: "cd",
child: TraceBundle {
name: "cd",
fields: [
TraceClock {
location: TraceScalarId(0),
name: "clk",
flow: Source,
},
TraceSyncReset {
location: TraceScalarId(1),
name: "rst",
flow: Source,
},
],
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: SyncReset,
},
flow: Source,
},
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: SyncReset,
},
flow: Source,
},
TraceModuleIO {
name: "count",
child: TraceUInt {
location: TraceScalarId(2),
name: "count",
ty: UInt<4>,
flow: Sink,
},
ty: UInt<4>,
flow: Sink,
},
TraceReg {
name: "count_reg",
child: TraceUInt {
location: TraceScalarId(3),
name: "count_reg",
ty: UInt<4>,
flow: Duplex,
},
ty: UInt<4>,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigClock {
index: StatePartIndex<BigSlots>(0),
},
state: 0x1,
last_state: 0x1,
},
SimTrace {
id: TraceScalarId(1),
kind: BigSyncReset {
index: StatePartIndex<BigSlots>(1),
},
state: 0x0,
last_state: 0x0,
},
SimTrace {
id: TraceScalarId(2),
kind: BigUInt {
index: StatePartIndex<BigSlots>(2),
ty: UInt<4>,
},
state: 0x3,
last_state: 0x2,
},
SimTrace {
id: TraceScalarId(3),
kind: BigUInt {
index: StatePartIndex<BigSlots>(3),
ty: UInt<4>,
},
state: 0x3,
last_state: 0x3,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 66 μs,
clocks_triggered: [
StatePartIndex<SmallSlots>(1),
],
..
}

View file

@ -1,214 +0,0 @@
$timescale 1 ps $end
$scope module counter $end
$scope struct cd $end
$var wire 1 ! clk $end
$var wire 1 " rst $end
$upscope $end
$var wire 4 # count $end
$var reg 4 $ count_reg $end
$upscope $end
$enddefinitions $end
$dumpvars
0!
1"
b0 #
b0 $
$end
#1000000
1!
b11 $
b11 #
0"
#2000000
0!
#3000000
1!
b100 $
b100 #
#4000000
0!
#5000000
1!
b101 $
b101 #
#6000000
0!
#7000000
1!
b110 $
b110 #
#8000000
0!
#9000000
1!
b111 $
b111 #
#10000000
0!
#11000000
1!
b1000 $
b1000 #
#12000000
0!
#13000000
1!
b1001 $
b1001 #
#14000000
0!
#15000000
1!
b1010 $
b1010 #
#16000000
0!
#17000000
1!
b1011 $
b1011 #
#18000000
0!
#19000000
1!
b1100 $
b1100 #
#20000000
0!
#21000000
1!
b1101 $
b1101 #
#22000000
0!
#23000000
1!
b1110 $
b1110 #
#24000000
0!
#25000000
1!
b1111 $
b1111 #
#26000000
0!
#27000000
1!
b0 $
b0 #
#28000000
0!
#29000000
1!
b1 $
b1 #
#30000000
0!
#31000000
1!
b10 $
b10 #
#32000000
0!
#33000000
1!
b11 $
b11 #
#34000000
0!
#35000000
1!
b100 $
b100 #
#36000000
0!
#37000000
1!
b101 $
b101 #
#38000000
0!
#39000000
1!
b110 $
b110 #
#40000000
0!
#41000000
1!
b111 $
b111 #
#42000000
0!
#43000000
1!
b1000 $
b1000 #
#44000000
0!
#45000000
1!
b1001 $
b1001 #
#46000000
0!
#47000000
1!
b1010 $
b1010 #
#48000000
0!
#49000000
1!
b1011 $
b1011 #
#50000000
0!
#51000000
1!
b1100 $
b1100 #
#52000000
0!
#53000000
1!
b1101 $
b1101 #
#54000000
0!
#55000000
1!
b1110 $
b1110 #
#56000000
0!
#57000000
1!
b1111 $
b1111 #
#58000000
0!
#59000000
1!
b0 $
b0 #
#60000000
0!
#61000000
1!
b1 $
b1 #
#62000000
0!
#63000000
1!
b10 $
b10 #
#64000000
0!
#65000000
1!
b11 $
b11 #
#66000000

View file

@ -1,153 +0,0 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 4,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
SlotDebugData {
name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> },
value: 0x6,
},
// at: module-XXXXXXXXXX.rs:5:1
1: Copy {
dest: StatePartIndex<BigSlots>(2), // (0x6) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> },
src: StatePartIndex<BigSlots>(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:1:1
2: Const {
dest: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> },
value: 0x5,
},
// at: module-XXXXXXXXXX.rs:3:1
3: Copy {
dest: StatePartIndex<BigSlots>(0), // (0x5) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> },
src: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:1:1
4: Return,
],
..
},
pc: 4,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [],
},
big_slots: StatePart {
value: [
5,
5,
6,
6,
],
},
},
io: Instance {
name: <simulator>::duplicate_names,
instantiated: Module {
name: duplicate_names,
..
},
},
uninitialized_inputs: {},
io_targets: {},
made_initial_step: true,
needs_settle: false,
trace_decls: TraceModule {
name: "duplicate_names",
children: [
TraceWire {
name: "w",
child: TraceUInt {
location: TraceScalarId(0),
name: "w",
ty: UInt<8>,
flow: Duplex,
},
ty: UInt<8>,
},
TraceWire {
name: "w",
child: TraceUInt {
location: TraceScalarId(1),
name: "w",
ty: UInt<8>,
flow: Duplex,
},
ty: UInt<8>,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigUInt {
index: StatePartIndex<BigSlots>(0),
ty: UInt<8>,
},
state: 0x05,
last_state: 0x05,
},
SimTrace {
id: TraceScalarId(1),
kind: BigUInt {
index: StatePartIndex<BigSlots>(2),
ty: UInt<8>,
},
state: 0x06,
last_state: 0x06,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 1 μs,
clocks_triggered: [],
..
}

View file

@ -1,11 +0,0 @@
$timescale 1 ps $end
$scope module duplicate_names $end
$var wire 8 ! w $end
$var wire 8 " w_2 $end
$upscope $end
$enddefinitions $end
$dumpvars
b101 !
b110 "
$end
#1000000

File diff suppressed because it is too large Load diff

View file

@ -1,110 +0,0 @@
$timescale 1 ps $end
$scope module enums $end
$scope struct cd $end
$var wire 1 ! clk $end
$var wire 1 " rst $end
$upscope $end
$var wire 1 # en $end
$var wire 2 $ which_in $end
$var wire 4 % data_in $end
$var wire 2 & which_out $end
$var wire 4 ' data_out $end
$scope struct b_out $end
$var string 1 ( \$tag $end
$scope struct HdlSome $end
$var wire 1 ) \0 $end
$var wire 1 * \1 $end
$upscope $end
$upscope $end
$scope struct the_reg $end
$var string 1 + \$tag $end
$scope struct B $end
$var reg 1 , \0 $end
$var reg 1 - \1 $end
$upscope $end
$scope struct C $end
$scope struct a $end
$var reg 1 . \[0] $end
$var reg 1 / \[1] $end
$upscope $end
$var reg 2 0 b $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
0!
1"
0#
b0 $
b0 %
b0 &
b0 '
sHdlNone\x20(0) (
0)
0*
sA\x20(0) +
0,
0-
0.
0/
b0 0
$end
#1000000
1!
#1100000
0"
#2000000
0!
#3000000
1!
#4000000
1#
b1 $
0!
#5000000
1!
b1 &
sHdlSome\x20(1) (
sB\x20(1) +
#6000000
0#
b0 $
0!
#7000000
1!
#8000000
1#
b1 $
b1111 %
0!
#9000000
1!
b11 '
1)
1*
1,
1-
1.
1/
#10000000
0!
#11000000
1!
#12000000
b10 $
0!
#13000000
1!
b10 &
b1111 '
sHdlNone\x20(0) (
0)
0*
sC\x20(2) +
b11 0
#14000000
0!
#15000000
1!
#16000000

File diff suppressed because it is too large Load diff

View file

@ -1,408 +0,0 @@
$timescale 1 ps $end
$scope module memories $end
$scope struct r $end
$var wire 4 ! addr $end
$var wire 1 " en $end
$var wire 1 # clk $end
$scope struct data $end
$var wire 8 $ \0 $end
$var wire 8 % \1 $end
$upscope $end
$upscope $end
$scope struct w $end
$var wire 4 & addr $end
$var wire 1 ' en $end
$var wire 1 ( clk $end
$scope struct data $end
$var wire 8 ) \0 $end
$var wire 8 * \1 $end
$upscope $end
$scope struct mask $end
$var wire 1 + \0 $end
$var wire 1 , \1 $end
$upscope $end
$upscope $end
$scope struct mem $end
$scope struct contents $end
$scope struct \[0] $end
$scope struct mem $end
$var reg 8 9 \0 $end
$var reg 8 I \1 $end
$upscope $end
$upscope $end
$scope struct \[1] $end
$scope struct mem $end
$var reg 8 : \0 $end
$var reg 8 J \1 $end
$upscope $end
$upscope $end
$scope struct \[2] $end
$scope struct mem $end
$var reg 8 ; \0 $end
$var reg 8 K \1 $end
$upscope $end
$upscope $end
$scope struct \[3] $end
$scope struct mem $end
$var reg 8 < \0 $end
$var reg 8 L \1 $end
$upscope $end
$upscope $end
$scope struct \[4] $end
$scope struct mem $end
$var reg 8 = \0 $end
$var reg 8 M \1 $end
$upscope $end
$upscope $end
$scope struct \[5] $end
$scope struct mem $end
$var reg 8 > \0 $end
$var reg 8 N \1 $end
$upscope $end
$upscope $end
$scope struct \[6] $end
$scope struct mem $end
$var reg 8 ? \0 $end
$var reg 8 O \1 $end
$upscope $end
$upscope $end
$scope struct \[7] $end
$scope struct mem $end
$var reg 8 @ \0 $end
$var reg 8 P \1 $end
$upscope $end
$upscope $end
$scope struct \[8] $end
$scope struct mem $end
$var reg 8 A \0 $end
$var reg 8 Q \1 $end
$upscope $end
$upscope $end
$scope struct \[9] $end
$scope struct mem $end
$var reg 8 B \0 $end
$var reg 8 R \1 $end
$upscope $end
$upscope $end
$scope struct \[10] $end
$scope struct mem $end
$var reg 8 C \0 $end
$var reg 8 S \1 $end
$upscope $end
$upscope $end
$scope struct \[11] $end
$scope struct mem $end
$var reg 8 D \0 $end
$var reg 8 T \1 $end
$upscope $end
$upscope $end
$scope struct \[12] $end
$scope struct mem $end
$var reg 8 E \0 $end
$var reg 8 U \1 $end
$upscope $end
$upscope $end
$scope struct \[13] $end
$scope struct mem $end
$var reg 8 F \0 $end
$var reg 8 V \1 $end
$upscope $end
$upscope $end
$scope struct \[14] $end
$scope struct mem $end
$var reg 8 G \0 $end
$var reg 8 W \1 $end
$upscope $end
$upscope $end
$scope struct \[15] $end
$scope struct mem $end
$var reg 8 H \0 $end
$var reg 8 X \1 $end
$upscope $end
$upscope $end
$upscope $end
$scope struct r0 $end
$var wire 4 - addr $end
$var wire 1 . en $end
$var wire 1 / clk $end
$scope struct data $end
$var wire 8 0 \0 $end
$var wire 8 1 \1 $end
$upscope $end
$upscope $end
$scope struct w1 $end
$var wire 4 2 addr $end
$var wire 1 3 en $end
$var wire 1 4 clk $end
$scope struct data $end
$var wire 8 5 \0 $end
$var wire 8 6 \1 $end
$upscope $end
$scope struct mask $end
$var wire 1 7 \0 $end
$var wire 1 8 \1 $end
$upscope $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
b1 9
b100011 I
b1 :
b100011 J
b1 ;
b100011 K
b1 <
b100011 L
b1 =
b100011 M
b1 >
b100011 N
b1 ?
b100011 O
b1 @
b100011 P
b1 A
b100011 Q
b1 B
b100011 R
b1 C
b100011 S
b1 D
b100011 T
b1 E
b100011 U
b1 F
b100011 V
b1 G
b100011 W
b1 H
b100011 X
b0 !
0"
0#
b0 $
b0 %
b0 &
0'
0(
b0 )
b0 *
0+
0,
b0 -
0.
0/
b0 0
b0 1
b0 2
03
04
b0 5
b0 6
07
08
$end
#1000000
1#
1(
1/
14
#2000000
1"
0#
b1 $
b100011 %
1'
0(
b10000 )
b100000 *
1+
1,
1.
0/
b1 0
b100011 1
13
04
b10000 5
b100000 6
17
18
#3000000
b10000 9
b100000 I
1#
1(
1/
14
b10000 $
b100000 %
b10000 0
b100000 1
#4000000
0#
0(
b110000 )
b1000000 *
0+
0/
04
b110000 5
b1000000 6
07
#5000000
b10000 9
b1000000 I
1#
1(
1/
14
b1000000 %
b1000000 1
#6000000
0#
0(
b1010000 )
b1100000 *
1+
0,
0/
04
b1010000 5
b1100000 6
17
08
#7000000
b1010000 9
b1000000 I
1#
1(
1/
14
b1010000 $
b1010000 0
#8000000
0#
0(
b1110000 )
b10000000 *
0+
0/
04
b1110000 5
b10000000 6
07
#9000000
1#
1(
1/
14
#10000000
0#
0'
0(
b10010000 )
b10100000 *
0/
03
04
b10010000 5
b10100000 6
#11000000
1#
1(
1/
14
#12000000
0#
b1 &
1'
0(
1+
1,
0/
b1 2
13
04
17
18
#13000000
b10010000 :
b10100000 J
1#
1(
1/
14
#14000000
0#
b10 &
0(
b10110000 )
b11000000 *
0/
b10 2
04
b10110000 5
b11000000 6
#15000000
b10110000 ;
b11000000 K
1#
1(
1/
14
#16000000
0#
0'
0(
b11010000 )
b11100000 *
0/
03
04
b11010000 5
b11100000 6
#17000000
1#
1(
1/
14
#18000000
b1 !
0#
b10010000 $
b10100000 %
0(
b1 -
0/
b10010000 0
b10100000 1
04
#19000000
1#
1(
1/
14
#20000000
b10 !
0#
b10110000 $
b11000000 %
0(
b10 -
0/
b10110000 0
b11000000 1
04
#21000000
1#
1(
1/
14
#22000000
0#
0(
0/
04

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