Compare commits
No commits in common. "master" and "master" have entirely different histories.
196 changed files with 9867 additions and 147653 deletions
77
.forgejo/workflows/deps.yml
Normal file
77
.forgejo/workflows/deps.yml
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
# See Notices.txt for copyright information
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
outputs:
|
||||||
|
cache-primary-key:
|
||||||
|
value: ${{ jobs.deps.outputs.cache-primary-key }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deps:
|
||||||
|
runs-on: debian-12
|
||||||
|
outputs:
|
||||||
|
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
||||||
|
steps:
|
||||||
|
- uses: https://git.libre-chip.org/mirrors/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
|
||||||
|
id: restore-deps
|
||||||
|
with:
|
||||||
|
path: deps
|
||||||
|
key: ${{ github.repository }}-deps-${{ runner.os }}-${{ hashFiles('.forgejo/workflows/deps.yml') }}
|
||||||
|
lookup-only: true
|
||||||
|
- name: Install Apt packages
|
||||||
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -qq \
|
||||||
|
bison \
|
||||||
|
build-essential \
|
||||||
|
ccache \
|
||||||
|
clang \
|
||||||
|
cvc5 \
|
||||||
|
flex \
|
||||||
|
gawk \
|
||||||
|
g++ \
|
||||||
|
git \
|
||||||
|
libboost-filesystem-dev \
|
||||||
|
libboost-python-dev \
|
||||||
|
libboost-system-dev \
|
||||||
|
libffi-dev \
|
||||||
|
libreadline-dev \
|
||||||
|
lld \
|
||||||
|
pkg-config \
|
||||||
|
python3 \
|
||||||
|
python3-click \
|
||||||
|
tcl-dev \
|
||||||
|
zlib1g-dev
|
||||||
|
- name: Install Firtool
|
||||||
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
mkdir -p deps
|
||||||
|
wget -O deps/firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz
|
||||||
|
sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 deps/firrtl.tar.gz'
|
||||||
|
tar -C deps -xvaf deps/firrtl.tar.gz
|
||||||
|
rm -rf deps/firtool
|
||||||
|
mv deps/firtool-1.86.0 deps/firtool
|
||||||
|
- name: Get SymbiYosys
|
||||||
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby
|
||||||
|
- name: Build Z3
|
||||||
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3
|
||||||
|
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
|
||||||
|
make -C deps/z3/build -j"$(nproc)"
|
||||||
|
- name: Build Yosys
|
||||||
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys
|
||||||
|
make -C deps/yosys -j"$(nproc)"
|
||||||
|
- uses: https://git.libre-chip.org/mirrors/cache/save@v3
|
||||||
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
path: deps
|
||||||
|
key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
||||||
|
|
@ -3,26 +3,62 @@
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
deps:
|
||||||
|
runs-on: debian-12
|
||||||
|
uses: ./.forgejo/workflows/deps.yml
|
||||||
test:
|
test:
|
||||||
runs-on: debian-12
|
runs-on: debian-12
|
||||||
container:
|
needs: deps
|
||||||
image: git.libre-chip.org/libre-chip/fayalite-deps:latest
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: https://git.libre-chip.org/mirrors/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: |
|
- run: |
|
||||||
scripts/check-copyright.sh
|
scripts/check-copyright.sh
|
||||||
|
- run: |
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -qq \
|
||||||
|
bison \
|
||||||
|
build-essential \
|
||||||
|
ccache \
|
||||||
|
clang \
|
||||||
|
cvc5 \
|
||||||
|
flex \
|
||||||
|
gawk \
|
||||||
|
git \
|
||||||
|
libboost-filesystem-dev \
|
||||||
|
libboost-python-dev \
|
||||||
|
libboost-system-dev \
|
||||||
|
libffi-dev \
|
||||||
|
libreadline-dev \
|
||||||
|
lld \
|
||||||
|
pkg-config \
|
||||||
|
python3 \
|
||||||
|
python3-click \
|
||||||
|
tcl-dev \
|
||||||
|
z3 \
|
||||||
|
zlib1g-dev
|
||||||
|
- run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.89.0
|
||||||
|
source "$HOME/.cargo/env"
|
||||||
|
rustup component add rust-src
|
||||||
|
echo "$PATH" >> "$GITHUB_PATH"
|
||||||
|
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
|
||||||
|
with:
|
||||||
|
path: deps
|
||||||
|
key: ${{ needs.deps.outputs.cache-primary-key }}
|
||||||
|
fail-on-cache-miss: true
|
||||||
|
- run: |
|
||||||
|
make -C deps/z3/build install
|
||||||
|
make -C deps/sby install
|
||||||
|
make -C deps/yosys install
|
||||||
|
export PATH="$(realpath deps/firtool/bin):$PATH"
|
||||||
|
echo "$PATH" >> "$GITHUB_PATH"
|
||||||
- uses: https://git.libre-chip.org/mirrors/rust-cache@v2
|
- uses: https://git.libre-chip.org/mirrors/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||||
- run: rustup override set 1.93.0
|
|
||||||
- run: rustup component add rust-src
|
|
||||||
- run: make -C rocq-demo
|
|
||||||
- run: cargo test
|
- run: cargo test
|
||||||
- run: cargo build --tests --features=unstable-doc
|
- run: cargo build --tests --features=unstable-doc
|
||||||
- run: cargo test --doc --features=unstable-doc
|
- run: cargo test --doc --features=unstable-doc
|
||||||
- run: cargo doc --features=unstable-doc
|
- run: cargo doc --features=unstable-doc
|
||||||
- run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher
|
- run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher
|
||||||
- run: cargo run --example blinky yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/blinky-out
|
|
||||||
- run: cargo run --example tx_only_uart yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/tx_only_uart-out
|
|
||||||
|
|
|
||||||
132
Cargo.lock
generated
132
Cargo.lock
generated
|
|
@ -1,6 +1,6 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
|
|
@ -25,9 +25,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.13"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
|
|
@ -81,12 +81,6 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64"
|
|
||||||
version = "0.22.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "basic-toml"
|
name = "basic-toml"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
|
|
@ -155,9 +149,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.48"
|
version = "4.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
|
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
|
@ -165,9 +159,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.48"
|
version = "4.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
|
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
|
@ -175,20 +169,11 @@ dependencies = [
|
||||||
"strsim",
|
"strsim",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_complete"
|
|
||||||
version = "4.5.58"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "75bf0b32ad2e152de789bb635ea4d3078f6b838ad7974143e99b99f45a04af4a"
|
|
||||||
dependencies = [
|
|
||||||
"clap",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.47"
|
version = "4.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
|
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|
@ -198,9 +183,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.5"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
|
|
@ -306,11 +291,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||||
name = "fayalite"
|
name = "fayalite"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
|
||||||
"bitvec",
|
"bitvec",
|
||||||
"blake3",
|
"blake3",
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
|
||||||
"ctor",
|
"ctor",
|
||||||
"eyre",
|
"eyre",
|
||||||
"fayalite-proc-macros",
|
"fayalite-proc-macros",
|
||||||
|
|
@ -319,12 +302,10 @@ dependencies = [
|
||||||
"jobslot",
|
"jobslot",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"os_pipe",
|
||||||
"ordered-float",
|
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"trybuild",
|
"trybuild",
|
||||||
"vec_map",
|
"vec_map",
|
||||||
|
|
@ -396,13 +377,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.3.3"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi",
|
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -469,23 +449,23 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobslot"
|
name = "jobslot"
|
||||||
version = "0.2.23"
|
version = "0.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c"
|
checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"derive_destructure2",
|
"derive_destructure2",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"libc",
|
"libc",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.176"
|
version = "0.2.153"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
|
|
@ -523,19 +503,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.3"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "os_pipe"
|
||||||
version = "5.1.0"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
|
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"libc",
|
||||||
"rand",
|
"windows-sys 0.59.0",
|
||||||
"serde",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -578,37 +557,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "r-efi"
|
|
||||||
version = "5.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "radium"
|
name = "radium"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.6.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.31"
|
version = "0.38.31"
|
||||||
|
|
@ -794,21 +748,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.14.7+wasi-0.2.4"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
dependencies = [
|
|
||||||
"wasip2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasip2"
|
|
||||||
version = "1.0.1+wasi-0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
|
||||||
dependencies = [
|
|
||||||
"wit-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
|
|
@ -853,12 +795,6 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-link"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
|
|
@ -870,11 +806,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.61.2"
|
version = "0.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -947,12 +883,6 @@ version = "0.0.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wit-bindgen"
|
|
||||||
version = "0.46.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wyz"
|
name = "wyz"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|
|
||||||
|
|
@ -11,27 +11,24 @@ edition = "2024"
|
||||||
repository = "https://git.libre-chip.org/libre-chip/fayalite"
|
repository = "https://git.libre-chip.org/libre-chip/fayalite"
|
||||||
keywords = ["hdl", "hardware", "semiconductors", "firrtl", "fpga"]
|
keywords = ["hdl", "hardware", "semiconductors", "firrtl", "fpga"]
|
||||||
categories = ["simulation", "development-tools", "compilers"]
|
categories = ["simulation", "development-tools", "compilers"]
|
||||||
rust-version = "1.93.0"
|
rust-version = "1.89.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
fayalite-proc-macros = { version = "=0.3.0", path = "crates/fayalite-proc-macros" }
|
fayalite-proc-macros = { version = "=0.3.0", path = "crates/fayalite-proc-macros" }
|
||||||
fayalite-proc-macros-impl = { version = "=0.3.0", path = "crates/fayalite-proc-macros-impl" }
|
fayalite-proc-macros-impl = { version = "=0.3.0", path = "crates/fayalite-proc-macros-impl" }
|
||||||
fayalite-visit-gen = { version = "=0.3.0", path = "crates/fayalite-visit-gen" }
|
fayalite-visit-gen = { version = "=0.3.0", path = "crates/fayalite-visit-gen" }
|
||||||
base16ct = "0.2.0"
|
base16ct = "0.2.0"
|
||||||
base64 = "0.22.1"
|
|
||||||
bitvec = { version = "1.0.1", features = ["serde"] }
|
bitvec = { version = "1.0.1", features = ["serde"] }
|
||||||
blake3 = { version = "1.5.4", features = ["serde"] }
|
blake3 = { version = "1.5.4", features = ["serde"] }
|
||||||
clap = { version = "4.5.9", features = ["derive", "env", "string"] }
|
clap = { version = "4.5.9", features = ["derive", "env", "string"] }
|
||||||
clap_complete = "4.5.58"
|
|
||||||
ctor = "0.2.8"
|
ctor = "0.2.8"
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
hashbrown = "0.15.2"
|
hashbrown = "0.15.2"
|
||||||
indexmap = { version = "2.5.0", features = ["serde"] }
|
indexmap = { version = "2.5.0", features = ["serde"] }
|
||||||
jobslot = "0.2.23"
|
jobslot = "0.2.19"
|
||||||
num-bigint = "0.4.6"
|
num-bigint = "0.4.6"
|
||||||
num-traits = "0.2.16"
|
num-traits = "0.2.16"
|
||||||
once_cell = "1.21.3"
|
os_pipe = "1.2.1"
|
||||||
ordered-float = { version = "5.1.0", features = ["serde"] }
|
|
||||||
petgraph = "0.8.1"
|
petgraph = "0.8.1"
|
||||||
prettyplease = "0.2.20"
|
prettyplease = "0.2.20"
|
||||||
proc-macro2 = "1.0.83"
|
proc-macro2 = "1.0.83"
|
||||||
|
|
|
||||||
67
README.md
67
README.md
|
|
@ -8,73 +8,6 @@ Fayalite is a library for designing digital hardware -- a hardware description l
|
||||||
|
|
||||||
[FIRRTL]: https://github.com/chipsalliance/firrtl-spec
|
[FIRRTL]: https://github.com/chipsalliance/firrtl-spec
|
||||||
|
|
||||||
# Building the [Blinky example] for the Arty A7 100T on Linux
|
|
||||||
|
|
||||||
[Blinky example]: crates/fayalite/examples/blinky.rs
|
|
||||||
|
|
||||||
This uses the container image containing all the external programs and files that Fayalite needs to build for FPGAs, the sources for the container image are in <https://git.libre-chip.org/libre-chip/fayalite-deps>
|
|
||||||
|
|
||||||
Steps:
|
|
||||||
|
|
||||||
Install podman (or docker).
|
|
||||||
|
|
||||||
Run:
|
|
||||||
```bash
|
|
||||||
podman run --rm --security-opt label=disable --volume="$(pwd):$(pwd)" -w="$(pwd)" -it git.libre-chip.org/libre-chip/fayalite-deps:latest cargo run --example blinky yosys-nextpnr-xray --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db --platform arty-a7-100t -o target/blinky-out
|
|
||||||
```
|
|
||||||
|
|
||||||
To actually program the FPGA, you'll need to install [openFPGALoader] on your host OS:
|
|
||||||
|
|
||||||
[openFPGALoader]: https://github.com/trabucayre/openFPGALoader
|
|
||||||
|
|
||||||
On Debian 12:
|
|
||||||
```bash
|
|
||||||
sudo apt update && sudo apt install openfpgaloader
|
|
||||||
```
|
|
||||||
|
|
||||||
Then program the FPGA:
|
|
||||||
```bash
|
|
||||||
sudo openFPGALoader --board arty_a7_100t target/blinky-out/blinky.bit
|
|
||||||
```
|
|
||||||
|
|
||||||
This will program the FPGA but leave the Flash chip unmodified, so the FPGA will revert when the board is power-cycled.
|
|
||||||
|
|
||||||
To program the Flash also, so it stays programmed when power-cycling the board:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo openFPGALoader --board arty_a7_100t -f target/blinky-out/blinky.bit
|
|
||||||
```
|
|
||||||
|
|
||||||
# Building the [Transmit-only UART example] for the Arty A7 100T on Linux
|
|
||||||
|
|
||||||
[Transmit-only UART example]: crates/fayalite/examples/tx_only_uart.rs
|
|
||||||
|
|
||||||
Follow the steps above of building the Blinky example, but replace `blinky` with `tx_only_uart`.
|
|
||||||
|
|
||||||
View the output using [tio](https://github.com/tio/tio) which you can install in Debian using `apt`.
|
|
||||||
|
|
||||||
Find the correct USB device:
|
|
||||||
```bash
|
|
||||||
sudo tio --list
|
|
||||||
```
|
|
||||||
|
|
||||||
You want the device with a name like (note the `if01`, `if00` is presumably the JTAG port):
|
|
||||||
`/dev/serial/by-id/usb-Digilent_Digilent_USB_Device_210319B4A51E-if01-port0`
|
|
||||||
|
|
||||||
Connect to the serial port:
|
|
||||||
```bash
|
|
||||||
sudo tio -b115200 /dev/serial/by-id/put-your-device-id-here
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll see (repeating endlessly):
|
|
||||||
```text
|
|
||||||
Hello World from Fayalite!!!
|
|
||||||
Hello World from Fayalite!!!
|
|
||||||
Hello World from Fayalite!!!
|
|
||||||
```
|
|
||||||
|
|
||||||
Press Ctrl+T then `q` to exit tio.
|
|
||||||
|
|
||||||
# Funding
|
# Funding
|
||||||
|
|
||||||
## NLnet Grants
|
## NLnet Grants
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,5 @@ no_op_fold!(syn::Token![let]);
|
||||||
no_op_fold!(syn::Token![mut]);
|
no_op_fold!(syn::Token![mut]);
|
||||||
no_op_fold!(syn::Token![static]);
|
no_op_fold!(syn::Token![static]);
|
||||||
no_op_fold!(syn::Token![struct]);
|
no_op_fold!(syn::Token![struct]);
|
||||||
no_op_fold!(syn::Token![type]);
|
|
||||||
no_op_fold!(syn::Token![where]);
|
no_op_fold!(syn::Token![where]);
|
||||||
no_op_fold!(usize);
|
no_op_fold!(usize);
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Errors, HdlAttr, PairsIterExt,
|
Errors, HdlAttr, PairsIterExt,
|
||||||
hdl_type_common::{
|
hdl_type_common::{
|
||||||
CustomDebugOptions, CustomDebugTrait, ItemOptions, MakeHdlTypeExpr, MaybeParsed,
|
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, ParsedFieldsNamed, ParsedGenerics,
|
||||||
ParsedField, ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst,
|
SplitForImpl, TypesParser, WrappedInConst, common_derives, get_target,
|
||||||
common_derives, create_struct_debug_impl, get_target,
|
|
||||||
},
|
},
|
||||||
kw,
|
kw,
|
||||||
};
|
};
|
||||||
|
|
@ -31,7 +30,6 @@ pub(crate) struct ParsedBundle {
|
||||||
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
|
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
|
||||||
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>,
|
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>,
|
||||||
pub(crate) mask_type_ident: Ident,
|
pub(crate) mask_type_ident: Ident,
|
||||||
pub(crate) mask_type_name: String,
|
|
||||||
pub(crate) mask_type_match_variant_ident: Ident,
|
pub(crate) mask_type_match_variant_ident: Ident,
|
||||||
pub(crate) mask_type_sim_value_ident: Ident,
|
pub(crate) mask_type_sim_value_ident: Ident,
|
||||||
pub(crate) match_variant_ident: Ident,
|
pub(crate) match_variant_ident: Ident,
|
||||||
|
|
@ -89,13 +87,7 @@ impl ParsedBundle {
|
||||||
no_static: _,
|
no_static: _,
|
||||||
no_runtime_generics: _,
|
no_runtime_generics: _,
|
||||||
cmp_eq: _,
|
cmp_eq: _,
|
||||||
ref get,
|
|
||||||
custom_debug: _,
|
|
||||||
custom_sim_display: _,
|
|
||||||
} = options.body;
|
} = options.body;
|
||||||
if let Some((get, ..)) = get {
|
|
||||||
errors.error(get, "#[hdl(get(...))] is not allowed on structs");
|
|
||||||
}
|
|
||||||
let mut fields = match fields {
|
let mut fields = match fields {
|
||||||
syn::Fields::Named(fields) => fields,
|
syn::Fields::Named(fields) => fields,
|
||||||
syn::Fields::Unnamed(fields) => {
|
syn::Fields::Unnamed(fields) => {
|
||||||
|
|
@ -135,7 +127,6 @@ impl ParsedBundle {
|
||||||
fields,
|
fields,
|
||||||
field_flips,
|
field_flips,
|
||||||
mask_type_ident: format_ident!("__{}__MaskType", ident),
|
mask_type_ident: format_ident!("__{}__MaskType", ident),
|
||||||
mask_type_name: format!("MaskType<{}>", ident),
|
|
||||||
mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident),
|
mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident),
|
||||||
mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident),
|
mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident),
|
||||||
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
|
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
|
||||||
|
|
@ -229,7 +220,7 @@ impl Builder {
|
||||||
.args
|
.args
|
||||||
.push_value(match get_field_state(field_index) {
|
.push_value(match get_field_state(field_index) {
|
||||||
BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=>
|
BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=>
|
||||||
()
|
::fayalite::bundle::Unfilled<#ty>
|
||||||
},
|
},
|
||||||
BuilderFieldState::Generic => {
|
BuilderFieldState::Generic => {
|
||||||
let type_var = type_var_for_field_name(ident);
|
let type_var = type_var_for_field_name(ident);
|
||||||
|
|
@ -354,6 +345,7 @@ impl ToTokens for Builder {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
quote_spanned! {self.ident.span()=>
|
quote_spanned! {self.ident.span()=>
|
||||||
|
#[automatically_derived]
|
||||||
#[allow(non_camel_case_types, non_snake_case, dead_code)]
|
#[allow(non_camel_case_types, non_snake_case, dead_code)]
|
||||||
impl #impl_generics #unfilled_ty
|
impl #impl_generics #unfilled_ty
|
||||||
#where_clause
|
#where_clause
|
||||||
|
|
@ -388,7 +380,7 @@ impl ToTokens for Builder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
#ident {
|
#ident {
|
||||||
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||||
#(#field_idents: (),)*
|
#(#field_idents: ::fayalite::__std::default::Default::default(),)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -400,30 +392,16 @@ impl ToTokens for Builder {
|
||||||
let type_generics = self.generics.split_for_impl().1;
|
let type_generics = self.generics.split_for_impl().1;
|
||||||
quote_spanned! {self.ident.span()=>
|
quote_spanned! {self.ident.span()=>
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
#[allow(non_camel_case_types, dead_code, private_interfaces)]
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
impl #filled_impl_generics ::fayalite::expr::ValueType for #filled_ty
|
|
||||||
#filled_where_clause
|
|
||||||
{
|
|
||||||
type Type = #target #type_generics;
|
|
||||||
type ValueCategory = ::fayalite::expr::value_category::ValueCategoryExpr;
|
|
||||||
|
|
||||||
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
|
|
||||||
#target {
|
|
||||||
#(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[automatically_derived]
|
|
||||||
#[allow(non_camel_case_types, dead_code, private_interfaces)]
|
|
||||||
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
|
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
|
||||||
#filled_where_clause
|
#filled_where_clause
|
||||||
{
|
{
|
||||||
|
type Type = #target #type_generics;
|
||||||
fn to_expr(
|
fn to_expr(
|
||||||
&self,
|
&self,
|
||||||
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ValueType>::Type> {
|
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> {
|
||||||
let __ty = #target {
|
let __ty = #target {
|
||||||
#(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)*
|
#(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)*
|
||||||
};
|
};
|
||||||
let __field_values = [
|
let __field_values = [
|
||||||
#(::fayalite::expr::Expr::canonical(self.#field_idents),)*
|
#(::fayalite::expr::Expr::canonical(self.#field_idents),)*
|
||||||
|
|
@ -453,7 +431,6 @@ impl ToTokens for ParsedBundle {
|
||||||
fields,
|
fields,
|
||||||
field_flips,
|
field_flips,
|
||||||
mask_type_ident,
|
mask_type_ident,
|
||||||
mask_type_name,
|
|
||||||
mask_type_match_variant_ident,
|
mask_type_match_variant_ident,
|
||||||
mask_type_sim_value_ident,
|
mask_type_sim_value_ident,
|
||||||
match_variant_ident,
|
match_variant_ident,
|
||||||
|
|
@ -469,21 +446,11 @@ impl ToTokens for ParsedBundle {
|
||||||
no_static,
|
no_static,
|
||||||
no_runtime_generics,
|
no_runtime_generics,
|
||||||
cmp_eq,
|
cmp_eq,
|
||||||
get: _,
|
|
||||||
custom_debug: _,
|
|
||||||
custom_sim_display,
|
|
||||||
} = &options.body;
|
} = &options.body;
|
||||||
let CustomDebugOptions {
|
|
||||||
type_: custom_debug_type,
|
|
||||||
sim: custom_debug_sim,
|
|
||||||
mask_type: custom_debug_mask_type,
|
|
||||||
mask_sim: custom_debug_mask_sim,
|
|
||||||
} = options.body.custom_debug();
|
|
||||||
let target = get_target(target, ident);
|
let target = get_target(target, ident);
|
||||||
let struct_name = ident.to_string();
|
|
||||||
let mut item_attrs = attrs.clone();
|
let mut item_attrs = attrs.clone();
|
||||||
item_attrs.push(common_derives(span, false));
|
item_attrs.push(common_derives(span));
|
||||||
let type_struct = ItemStruct {
|
ItemStruct {
|
||||||
attrs: item_attrs,
|
attrs: item_attrs,
|
||||||
vis: vis.clone(),
|
vis: vis.clone(),
|
||||||
struct_token: *struct_token,
|
struct_token: *struct_token,
|
||||||
|
|
@ -491,8 +458,8 @@ impl ToTokens for ParsedBundle {
|
||||||
generics: generics.into(),
|
generics: generics.into(),
|
||||||
fields: Fields::Named(fields.clone().into()),
|
fields: Fields::Named(fields.clone().into()),
|
||||||
semi_token: None,
|
semi_token: None,
|
||||||
};
|
}
|
||||||
type_struct.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||||
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields), None) =
|
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields), None) =
|
||||||
(generics, fields, no_runtime_generics)
|
(generics, fields, no_runtime_generics)
|
||||||
|
|
@ -518,9 +485,6 @@ impl ToTokens for ParsedBundle {
|
||||||
}
|
}
|
||||||
let mut wrapped_in_const = WrappedInConst::new(tokens, span);
|
let mut wrapped_in_const = WrappedInConst::new(tokens, span);
|
||||||
let tokens = wrapped_in_const.inner();
|
let tokens = wrapped_in_const.inner();
|
||||||
if custom_debug_type.is_none() {
|
|
||||||
create_struct_debug_impl(&type_struct, &struct_name, None).to_tokens(tokens);
|
|
||||||
}
|
|
||||||
let builder = Builder {
|
let builder = Builder {
|
||||||
vis: vis.clone(),
|
vis: vis.clone(),
|
||||||
struct_token: *struct_token,
|
struct_token: *struct_token,
|
||||||
|
|
@ -531,6 +495,7 @@ impl ToTokens for ParsedBundle {
|
||||||
};
|
};
|
||||||
builder.to_tokens(tokens);
|
builder.to_tokens(tokens);
|
||||||
let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||||
|
let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||||
let mut mask_type_fields = FieldsNamed::from(fields.clone());
|
let mut mask_type_fields = FieldsNamed::from(fields.clone());
|
||||||
for Field { ty, .. } in &mut mask_type_fields.named {
|
for Field { ty, .. } in &mut mask_type_fields.named {
|
||||||
*ty = parse_quote_spanned! {span=>
|
*ty = parse_quote_spanned! {span=>
|
||||||
|
|
@ -548,9 +513,11 @@ impl ToTokens for ParsedBundle {
|
||||||
mask_type_builder.to_tokens(tokens);
|
mask_type_builder.to_tokens(tokens);
|
||||||
let unfilled_mask_type_builder_ty =
|
let unfilled_mask_type_builder_ty =
|
||||||
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||||
let mask_type_struct = ItemStruct {
|
let filled_mask_type_builder_ty =
|
||||||
|
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||||
|
ItemStruct {
|
||||||
attrs: vec![
|
attrs: vec![
|
||||||
common_derives(span, false),
|
common_derives(span),
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
},
|
},
|
||||||
|
|
@ -561,20 +528,17 @@ impl ToTokens for ParsedBundle {
|
||||||
generics: generics.into(),
|
generics: generics.into(),
|
||||||
fields: Fields::Named(mask_type_fields.clone()),
|
fields: Fields::Named(mask_type_fields.clone()),
|
||||||
semi_token: None,
|
semi_token: None,
|
||||||
};
|
|
||||||
mask_type_struct.to_tokens(tokens);
|
|
||||||
if custom_debug_mask_type.is_none() {
|
|
||||||
create_struct_debug_impl(&mask_type_struct, mask_type_name, None).to_tokens(tokens);
|
|
||||||
}
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
let mut mask_type_match_variant_fields = mask_type_fields.clone();
|
let mut mask_type_match_variant_fields = mask_type_fields.clone();
|
||||||
for Field { ty, .. } in &mut mask_type_match_variant_fields.named {
|
for Field { ty, .. } in &mut mask_type_match_variant_fields.named {
|
||||||
*ty = parse_quote_spanned! {span=>
|
*ty = parse_quote_spanned! {span=>
|
||||||
::fayalite::expr::Expr<#ty>
|
::fayalite::expr::Expr<#ty>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let mask_type_match_variant_struct = ItemStruct {
|
ItemStruct {
|
||||||
attrs: vec![
|
attrs: vec![
|
||||||
common_derives(span, false),
|
common_derives(span),
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
},
|
},
|
||||||
|
|
@ -585,9 +549,7 @@ impl ToTokens for ParsedBundle {
|
||||||
generics: generics.into(),
|
generics: generics.into(),
|
||||||
fields: Fields::Named(mask_type_match_variant_fields),
|
fields: Fields::Named(mask_type_match_variant_fields),
|
||||||
semi_token: None,
|
semi_token: None,
|
||||||
};
|
}
|
||||||
mask_type_match_variant_struct.to_tokens(tokens);
|
|
||||||
create_struct_debug_impl(&mask_type_match_variant_struct, mask_type_name, None)
|
|
||||||
.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
let mut match_variant_fields = FieldsNamed::from(fields.clone());
|
let mut match_variant_fields = FieldsNamed::from(fields.clone());
|
||||||
for Field { ty, .. } in &mut match_variant_fields.named {
|
for Field { ty, .. } in &mut match_variant_fields.named {
|
||||||
|
|
@ -595,9 +557,9 @@ impl ToTokens for ParsedBundle {
|
||||||
::fayalite::expr::Expr<#ty>
|
::fayalite::expr::Expr<#ty>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let match_variant_struct = ItemStruct {
|
ItemStruct {
|
||||||
attrs: vec![
|
attrs: vec![
|
||||||
common_derives(span, false),
|
common_derives(span),
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
},
|
},
|
||||||
|
|
@ -608,19 +570,19 @@ impl ToTokens for ParsedBundle {
|
||||||
generics: generics.into(),
|
generics: generics.into(),
|
||||||
fields: Fields::Named(match_variant_fields),
|
fields: Fields::Named(match_variant_fields),
|
||||||
semi_token: None,
|
semi_token: None,
|
||||||
};
|
}
|
||||||
match_variant_struct.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
create_struct_debug_impl(&match_variant_struct, &struct_name, None).to_tokens(tokens);
|
|
||||||
let mut mask_type_sim_value_fields = mask_type_fields;
|
let mut mask_type_sim_value_fields = mask_type_fields;
|
||||||
for Field { ty, .. } in &mut mask_type_sim_value_fields.named {
|
for Field { ty, .. } in &mut mask_type_sim_value_fields.named {
|
||||||
*ty = parse_quote_spanned! {span=>
|
*ty = parse_quote_spanned! {span=>
|
||||||
::fayalite::sim::value::SimValue<#ty>
|
::fayalite::sim::value::SimValue<#ty>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let mask_type_sim_value_struct = ItemStruct {
|
ItemStruct {
|
||||||
attrs: vec![
|
attrs: vec![
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
#[::fayalite::__std::prelude::v1::derive(
|
#[::fayalite::__std::prelude::v1::derive(
|
||||||
|
::fayalite::__std::fmt::Debug,
|
||||||
::fayalite::__std::clone::Clone,
|
::fayalite::__std::clone::Clone,
|
||||||
)]
|
)]
|
||||||
},
|
},
|
||||||
|
|
@ -634,34 +596,19 @@ impl ToTokens for ParsedBundle {
|
||||||
generics: generics.into(),
|
generics: generics.into(),
|
||||||
fields: Fields::Named(mask_type_sim_value_fields),
|
fields: Fields::Named(mask_type_sim_value_fields),
|
||||||
semi_token: None,
|
semi_token: None,
|
||||||
};
|
|
||||||
mask_type_sim_value_struct.to_tokens(tokens);
|
|
||||||
if custom_debug_mask_sim.is_none() {
|
|
||||||
create_struct_debug_impl(
|
|
||||||
&mask_type_struct,
|
|
||||||
mask_type_name,
|
|
||||||
Some(CustomDebugTrait {
|
|
||||||
trait_path: &parse_quote_spanned! {span=>
|
|
||||||
::fayalite::ty::SimValueDebug
|
|
||||||
},
|
|
||||||
fn_name: &format_ident!("sim_value_debug", span = span),
|
|
||||||
this_arg: &parse_quote_spanned! {span=>
|
|
||||||
value: &<Self as ::fayalite::ty::Type>::SimValue
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.to_tokens(tokens);
|
|
||||||
}
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
let mut sim_value_fields = FieldsNamed::from(fields.clone());
|
let mut sim_value_fields = FieldsNamed::from(fields.clone());
|
||||||
for Field { ty, .. } in &mut sim_value_fields.named {
|
for Field { ty, .. } in &mut sim_value_fields.named {
|
||||||
*ty = parse_quote_spanned! {span=>
|
*ty = parse_quote_spanned! {span=>
|
||||||
::fayalite::sim::value::SimValue<#ty>
|
::fayalite::sim::value::SimValue<#ty>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let sim_value_struct = ItemStruct {
|
ItemStruct {
|
||||||
attrs: vec![
|
attrs: vec![
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
#[::fayalite::__std::prelude::v1::derive(
|
#[::fayalite::__std::prelude::v1::derive(
|
||||||
|
::fayalite::__std::fmt::Debug,
|
||||||
::fayalite::__std::clone::Clone,
|
::fayalite::__std::clone::Clone,
|
||||||
)]
|
)]
|
||||||
},
|
},
|
||||||
|
|
@ -675,36 +622,8 @@ impl ToTokens for ParsedBundle {
|
||||||
generics: generics.into(),
|
generics: generics.into(),
|
||||||
fields: Fields::Named(sim_value_fields),
|
fields: Fields::Named(sim_value_fields),
|
||||||
semi_token: None,
|
semi_token: None,
|
||||||
};
|
}
|
||||||
sim_value_struct.to_tokens(tokens);
|
|
||||||
if custom_debug_sim.is_none() {
|
|
||||||
create_struct_debug_impl(
|
|
||||||
&type_struct,
|
|
||||||
&struct_name,
|
|
||||||
Some(CustomDebugTrait {
|
|
||||||
trait_path: &parse_quote_spanned! {span=>
|
|
||||||
::fayalite::ty::SimValueDebug
|
|
||||||
},
|
|
||||||
fn_name: &format_ident!("sim_value_debug", span = span),
|
|
||||||
this_arg: &parse_quote_spanned! {span=>
|
|
||||||
value: &<Self as ::fayalite::ty::Type>::SimValue
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
}
|
|
||||||
if custom_sim_display.is_some() {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
|
|
||||||
<#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
let this_token = Ident::new("__this", span);
|
let this_token = Ident::new("__this", span);
|
||||||
let fields_token = Ident::new("__fields", span);
|
let fields_token = Ident::new("__fields", span);
|
||||||
let self_token = Token;
|
let self_token = Token;
|
||||||
|
|
@ -775,10 +694,10 @@ impl ToTokens for ParsedBundle {
|
||||||
v.field(&value.#ident);
|
v.field(&value.#ident);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
let value_type_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
#ident: ::fayalite::expr::ValueType::ty(&self.#ident),
|
#ident: ::fayalite::sim::value::SimValue::ty(&self.#ident),
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
let fields_len = fields.named().into_iter().len();
|
let fields_len = fields.named().into_iter().len();
|
||||||
|
|
@ -862,6 +781,7 @@ impl ToTokens for ParsedBundle {
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
type Builder = #unfilled_mask_type_builder_ty;
|
type Builder = #unfilled_mask_type_builder_ty;
|
||||||
|
type FilledBuilder = #filled_mask_type_builder_ty;
|
||||||
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||||
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||||
}
|
}
|
||||||
|
|
@ -886,47 +806,28 @@ impl ToTokens for ParsedBundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #impl_generics ::fayalite::__std::fmt::Debug for #mask_type_sim_value_ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
|
|
||||||
<#mask_type_ident #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::expr::ValueType for #mask_type_sim_value_ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
type Type = #mask_type_ident #type_generics;
|
|
||||||
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
|
|
||||||
|
|
||||||
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
|
|
||||||
#mask_type_ident {
|
|
||||||
#(#value_type_fields)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics
|
impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
|
type Type = #mask_type_ident #type_generics;
|
||||||
|
|
||||||
fn to_sim_value(
|
fn to_sim_value(
|
||||||
&self,
|
&self,
|
||||||
) -> ::fayalite::sim::value::SimValue<
|
) -> ::fayalite::sim::value::SimValue<
|
||||||
<Self as ::fayalite::expr::ValueType>::Type,
|
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||||
> {
|
> {
|
||||||
let ty = #mask_type_ident {
|
let ty = #mask_type_ident {
|
||||||
#(#value_type_fields)*
|
#(#to_sim_value_fields)*
|
||||||
};
|
};
|
||||||
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
|
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
|
||||||
}
|
}
|
||||||
fn into_sim_value(
|
fn into_sim_value(
|
||||||
self,
|
self,
|
||||||
) -> ::fayalite::sim::value::SimValue<
|
) -> ::fayalite::sim::value::SimValue<
|
||||||
<Self as ::fayalite::expr::ValueType>::Type,
|
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||||
> {
|
> {
|
||||||
let ty = #mask_type_ident {
|
let ty = #mask_type_ident {
|
||||||
#(#value_type_fields)*
|
#(#to_sim_value_fields)*
|
||||||
};
|
};
|
||||||
::fayalite::sim::value::SimValue::from_value(ty, self)
|
::fayalite::sim::value::SimValue::from_value(ty, self)
|
||||||
}
|
}
|
||||||
|
|
@ -1030,6 +931,7 @@ impl ToTokens for ParsedBundle {
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
type Builder = #unfilled_builder_ty;
|
type Builder = #unfilled_builder_ty;
|
||||||
|
type FilledBuilder = #filled_builder_ty;
|
||||||
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||||
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||||
}
|
}
|
||||||
|
|
@ -1054,47 +956,28 @@ impl ToTokens for ParsedBundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
|
|
||||||
<#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::expr::ValueType for #sim_value_ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
type Type = #target #type_generics;
|
|
||||||
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
|
|
||||||
|
|
||||||
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
|
|
||||||
#target {
|
|
||||||
#(#value_type_fields)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
|
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
|
type Type = #target #type_generics;
|
||||||
|
|
||||||
fn to_sim_value(
|
fn to_sim_value(
|
||||||
&self,
|
&self,
|
||||||
) -> ::fayalite::sim::value::SimValue<
|
) -> ::fayalite::sim::value::SimValue<
|
||||||
<Self as ::fayalite::expr::ValueType>::Type,
|
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||||
> {
|
> {
|
||||||
let ty = #target {
|
let ty = #target {
|
||||||
#(#value_type_fields)*
|
#(#to_sim_value_fields)*
|
||||||
};
|
};
|
||||||
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
|
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
|
||||||
}
|
}
|
||||||
fn into_sim_value(
|
fn into_sim_value(
|
||||||
self,
|
self,
|
||||||
) -> ::fayalite::sim::value::SimValue<
|
) -> ::fayalite::sim::value::SimValue<
|
||||||
<Self as ::fayalite::expr::ValueType>::Type,
|
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||||
> {
|
> {
|
||||||
let ty = #target {
|
let ty = #target {
|
||||||
#(#value_type_fields)*
|
#(#to_sim_value_fields)*
|
||||||
};
|
};
|
||||||
::fayalite::sim::value::SimValue::from_value(ty, self)
|
::fayalite::sim::value::SimValue::from_value(ty, self)
|
||||||
}
|
}
|
||||||
|
|
@ -1120,203 +1003,91 @@ impl ToTokens for ParsedBundle {
|
||||||
}
|
}
|
||||||
.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
if let Some((cmp_eq,)) = cmp_eq {
|
if let Some((cmp_eq,)) = cmp_eq {
|
||||||
let mut cmp_eq_where_clause =
|
let mut expr_where_clause =
|
||||||
Generics::from(generics)
|
Generics::from(generics)
|
||||||
.where_clause
|
.where_clause
|
||||||
.unwrap_or_else(|| syn::WhereClause {
|
.unwrap_or_else(|| syn::WhereClause {
|
||||||
where_token: Token,
|
where_token: Token,
|
||||||
predicates: Punctuated::new(),
|
predicates: Punctuated::new(),
|
||||||
});
|
});
|
||||||
let mut fields_value_eq = vec![];
|
let mut sim_value_where_clause = expr_where_clause.clone();
|
||||||
let mut fields_value_ne = vec![];
|
let mut fields_sim_value_eq = vec![];
|
||||||
let mut fields_expr_eq = vec![];
|
let mut fields_cmp_eq = vec![];
|
||||||
let mut fields_expr_ne = vec![];
|
let mut fields_cmp_ne = vec![];
|
||||||
let mut fields_valueless_eq = vec![];
|
|
||||||
let mut fields_valueless_ne = vec![];
|
|
||||||
let mut fields_structural_eq = vec![];
|
|
||||||
for field in fields.named() {
|
for field in fields.named() {
|
||||||
let field_ident = field.ident();
|
let field_ident = field.ident();
|
||||||
let field_ty = field.ty();
|
let field_ty = field.ty();
|
||||||
cmp_eq_where_clause
|
expr_where_clause
|
||||||
.predicates
|
.predicates
|
||||||
.push(parse_quote_spanned! {cmp_eq.span=>
|
.push(parse_quote_spanned! {cmp_eq.span=>
|
||||||
#field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty>
|
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
|
||||||
});
|
});
|
||||||
fields_structural_eq.push(quote_spanned! {cmp_eq.span=>
|
sim_value_where_clause
|
||||||
<#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ
|
.predicates
|
||||||
|
.push(parse_quote_spanned! {cmp_eq.span=>
|
||||||
|
#field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty>
|
||||||
});
|
});
|
||||||
fields_value_eq.push(quote_spanned! {span=>
|
fields_sim_value_eq.push(quote_spanned! {span=>
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_value_eq(
|
::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident)
|
||||||
__lhs.#field_ident,
|
|
||||||
::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident),
|
|
||||||
__rhs.#field_ident,
|
|
||||||
::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
fields_value_ne.push(quote_spanned! {span=>
|
fields_cmp_eq.push(quote_spanned! {span=>
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_value_ne(
|
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
|
||||||
__lhs.#field_ident,
|
|
||||||
::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident),
|
|
||||||
__rhs.#field_ident,
|
|
||||||
::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
fields_expr_eq.push(quote_spanned! {span=>
|
fields_cmp_ne.push(quote_spanned! {span=>
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(
|
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
|
||||||
__lhs.#field_ident,
|
|
||||||
__rhs.#field_ident,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
fields_expr_ne.push(quote_spanned! {span=>
|
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_expr_ne(
|
|
||||||
__lhs.#field_ident,
|
|
||||||
__rhs.#field_ident,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
fields_valueless_eq.push(quote_spanned! {span=>
|
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq(
|
|
||||||
::fayalite::expr::Valueless::new(__lhs.#field_ident),
|
|
||||||
::fayalite::expr::Valueless::new(__rhs.#field_ident),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
fields_valueless_ne.push(quote_spanned! {span=>
|
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_ne(
|
|
||||||
::fayalite::expr::Valueless::new(__lhs.#field_ident),
|
|
||||||
::fayalite::expr::Valueless::new(__rhs.#field_ident),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let value_eq_body;
|
let sim_value_eq_body;
|
||||||
let value_ne_body;
|
let cmp_eq_body;
|
||||||
let expr_eq_body;
|
let cmp_ne_body;
|
||||||
let expr_ne_body;
|
|
||||||
let valueless_eq_body;
|
|
||||||
let valueless_ne_body;
|
|
||||||
let structural_eq;
|
|
||||||
if fields_len == 0 {
|
if fields_len == 0 {
|
||||||
value_eq_body = quote_spanned! {span=>
|
sim_value_eq_body = quote_spanned! {span=>
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
value_ne_body = quote_spanned! {span=>
|
cmp_eq_body = quote_spanned! {span=>
|
||||||
false
|
|
||||||
};
|
|
||||||
expr_eq_body = quote_spanned! {span=>
|
|
||||||
::fayalite::expr::ToExpr::to_expr(&true)
|
::fayalite::expr::ToExpr::to_expr(&true)
|
||||||
};
|
};
|
||||||
expr_ne_body = quote_spanned! {span=>
|
cmp_ne_body = quote_spanned! {span=>
|
||||||
::fayalite::expr::ToExpr::to_expr(&false)
|
::fayalite::expr::ToExpr::to_expr(&false)
|
||||||
};
|
};
|
||||||
valueless_eq_body = quote_spanned! {span=>
|
|
||||||
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
|
|
||||||
};
|
|
||||||
valueless_ne_body = quote_spanned! {span=>
|
|
||||||
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
|
|
||||||
};
|
|
||||||
structural_eq = quote_spanned! {span=>
|
|
||||||
true
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
value_eq_body = quote_spanned! {span=>
|
sim_value_eq_body = quote_spanned! {span=>
|
||||||
#(#fields_value_eq)&*
|
#(#fields_sim_value_eq)&&*
|
||||||
};
|
};
|
||||||
value_ne_body = quote_spanned! {span=>
|
cmp_eq_body = quote_spanned! {span=>
|
||||||
#(#fields_value_ne)|*
|
#(#fields_cmp_eq)&*
|
||||||
};
|
};
|
||||||
expr_eq_body = quote_spanned! {span=>
|
cmp_ne_body = quote_spanned! {span=>
|
||||||
#(#fields_expr_eq)&*
|
#(#fields_cmp_ne)|*
|
||||||
};
|
|
||||||
expr_ne_body = quote_spanned! {span=>
|
|
||||||
#(#fields_expr_ne)|*
|
|
||||||
};
|
|
||||||
valueless_eq_body = quote_spanned! {span=>
|
|
||||||
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
|
|
||||||
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
|
|
||||||
#(#fields_valueless_eq)&*
|
|
||||||
};
|
|
||||||
valueless_ne_body = quote_spanned! {span=>
|
|
||||||
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
|
|
||||||
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
|
|
||||||
#(#fields_valueless_ne)|*
|
|
||||||
};
|
|
||||||
structural_eq = quote_spanned! {span=>
|
|
||||||
#(#fields_structural_eq)&&*
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #impl_generics ::fayalite::expr::HdlPartialEqImpl<Self> for #target #type_generics
|
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
|
||||||
#cmp_eq_where_clause
|
#expr_where_clause
|
||||||
{
|
{
|
||||||
const TRY_STRUCTURAL_EQ: ::fayalite::__std::primitive::bool = #structural_eq;
|
fn cmp_eq(
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_eq(
|
|
||||||
__lhs: Self,
|
|
||||||
__lhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
|
|
||||||
__rhs: Self,
|
|
||||||
__rhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
|
|
||||||
) -> ::fayalite::__std::primitive::bool {
|
|
||||||
#value_eq_body
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_ne(
|
|
||||||
__lhs: Self,
|
|
||||||
__lhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
|
|
||||||
__rhs: Self,
|
|
||||||
__rhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
|
|
||||||
) -> ::fayalite::__std::primitive::bool {
|
|
||||||
#value_ne_body
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_eq(
|
|
||||||
__lhs: ::fayalite::expr::Expr<Self>,
|
__lhs: ::fayalite::expr::Expr<Self>,
|
||||||
__rhs: ::fayalite::expr::Expr<Self>,
|
__rhs: ::fayalite::expr::Expr<Self>,
|
||||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||||
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::TRY_STRUCTURAL_EQ {
|
#cmp_eq_body
|
||||||
if let ::fayalite::__std::result::Result::Ok(__retval) =
|
|
||||||
::fayalite::expr::ops::StructuralEq::try_new(
|
|
||||||
::fayalite::expr::Expr::canonical(__lhs),
|
|
||||||
::fayalite::expr::Expr::canonical(__rhs),
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return ::fayalite::expr::ToExpr::to_expr(&__retval);
|
|
||||||
}
|
}
|
||||||
}
|
fn cmp_ne(
|
||||||
#expr_eq_body
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_ne(
|
|
||||||
__lhs: ::fayalite::expr::Expr<Self>,
|
__lhs: ::fayalite::expr::Expr<Self>,
|
||||||
__rhs: ::fayalite::expr::Expr<Self>,
|
__rhs: ::fayalite::expr::Expr<Self>,
|
||||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||||
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::TRY_STRUCTURAL_EQ {
|
#cmp_ne_body
|
||||||
return !::fayalite::expr::ToExpr::to_expr(
|
|
||||||
&::fayalite::expr::ops::StructuralEq::new(
|
|
||||||
::fayalite::expr::Expr::canonical(__lhs),
|
|
||||||
::fayalite::expr::Expr::canonical(__rhs),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
#expr_ne_body
|
|
||||||
}
|
}
|
||||||
|
#[automatically_derived]
|
||||||
#[track_caller]
|
impl #impl_generics ::fayalite::sim::value::SimValuePartialEq<Self> for #target #type_generics
|
||||||
fn cmp_valueless_eq(
|
#sim_value_where_clause
|
||||||
__lhs: ::fayalite::expr::Valueless<Self>,
|
{
|
||||||
__rhs: ::fayalite::expr::Valueless<Self>,
|
fn sim_value_eq(
|
||||||
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
|
__lhs: &::fayalite::sim::value::SimValue<Self>,
|
||||||
#valueless_eq_body
|
__rhs: &::fayalite::sim::value::SimValue<Self>,
|
||||||
}
|
) -> bool {
|
||||||
|
#sim_value_eq_body
|
||||||
#[track_caller]
|
|
||||||
fn cmp_valueless_ne(
|
|
||||||
__lhs: ::fayalite::expr::Valueless<Self>,
|
|
||||||
__rhs: ::fayalite::expr::Valueless<Self>,
|
|
||||||
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
|
|
||||||
#valueless_ne_body
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Errors, HdlAttr, PairsIterExt,
|
Errors, HdlAttr, PairsIterExt,
|
||||||
hdl_type_common::{
|
hdl_type_common::{
|
||||||
CustomDebugOptions, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType,
|
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, SplitForImpl,
|
||||||
SplitForImpl, TypesParser, WrappedInConst, common_derives, create_struct_debug_impl,
|
TypesParser, WrappedInConst, common_derives, get_target,
|
||||||
get_target,
|
|
||||||
},
|
},
|
||||||
kw,
|
kw,
|
||||||
};
|
};
|
||||||
|
|
@ -159,31 +158,10 @@ impl ParsedEnum {
|
||||||
custom_bounds,
|
custom_bounds,
|
||||||
no_static: _,
|
no_static: _,
|
||||||
no_runtime_generics: _,
|
no_runtime_generics: _,
|
||||||
cmp_eq: _,
|
cmp_eq,
|
||||||
ref get,
|
|
||||||
custom_debug: _,
|
|
||||||
custom_sim_display: _,
|
|
||||||
} = options.body;
|
} = options.body;
|
||||||
if let Some((get, ..)) = get {
|
if let Some((cmp_eq,)) = cmp_eq {
|
||||||
errors.error(get, "#[hdl(get(...))] is not allowed on enums");
|
errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
|
||||||
}
|
|
||||||
let CustomDebugOptions {
|
|
||||||
type_: _,
|
|
||||||
sim: _,
|
|
||||||
mask_type,
|
|
||||||
mask_sim,
|
|
||||||
} = options.body.custom_debug();
|
|
||||||
if let Some((mask_type,)) = mask_type {
|
|
||||||
errors.error(
|
|
||||||
mask_type,
|
|
||||||
"#[hdl(custom_debug(mask_type)] is not allowed on enums",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some((mask_sim,)) = mask_sim {
|
|
||||||
errors.error(
|
|
||||||
mask_sim,
|
|
||||||
"#[hdl(custom_debug(mask_sim)] is not allowed on enums",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
attrs.retain(|attr| {
|
attrs.retain(|attr| {
|
||||||
if attr.path().is_ident("repr") {
|
if attr.path().is_ident("repr") {
|
||||||
|
|
@ -246,21 +224,11 @@ impl ToTokens for ParsedEnum {
|
||||||
custom_bounds: _,
|
custom_bounds: _,
|
||||||
no_static,
|
no_static,
|
||||||
no_runtime_generics,
|
no_runtime_generics,
|
||||||
cmp_eq,
|
cmp_eq: _, // TODO: implement cmp_eq for enums
|
||||||
get: _,
|
|
||||||
custom_debug: _,
|
|
||||||
custom_sim_display,
|
|
||||||
} = &options.body;
|
} = &options.body;
|
||||||
let CustomDebugOptions {
|
|
||||||
type_: custom_debug_type,
|
|
||||||
sim: custom_debug_sim,
|
|
||||||
mask_type: _,
|
|
||||||
mask_sim: _,
|
|
||||||
} = options.body.custom_debug();
|
|
||||||
let target = get_target(target, ident);
|
let target = get_target(target, ident);
|
||||||
let enum_name = ident.to_string();
|
|
||||||
let mut struct_attrs = attrs.clone();
|
let mut struct_attrs = attrs.clone();
|
||||||
struct_attrs.push(common_derives(span, false));
|
struct_attrs.push(common_derives(span));
|
||||||
struct_attrs.push(parse_quote_spanned! {span=>
|
struct_attrs.push(parse_quote_spanned! {span=>
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
});
|
});
|
||||||
|
|
@ -300,7 +268,7 @@ impl ToTokens for ParsedEnum {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
let type_struct = ItemStruct {
|
ItemStruct {
|
||||||
attrs: struct_attrs,
|
attrs: struct_attrs,
|
||||||
vis: vis.clone(),
|
vis: vis.clone(),
|
||||||
struct_token: Token,
|
struct_token: Token,
|
||||||
|
|
@ -315,8 +283,8 @@ impl ToTokens for ParsedEnum {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
semi_token: None,
|
semi_token: None,
|
||||||
};
|
}
|
||||||
type_struct.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||||
if let (MaybeParsed::Parsed(generics), None) = (generics, no_runtime_generics) {
|
if let (MaybeParsed::Parsed(generics), None) = (generics, no_runtime_generics) {
|
||||||
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
|
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
|
||||||
|
|
@ -400,9 +368,6 @@ impl ToTokens for ParsedEnum {
|
||||||
}
|
}
|
||||||
.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
if custom_debug_type.is_none() {
|
|
||||||
create_struct_debug_impl(&type_struct, &enum_name, None).to_tokens(tokens);
|
|
||||||
}
|
|
||||||
let mut enum_attrs = attrs.clone();
|
let mut enum_attrs = attrs.clone();
|
||||||
enum_attrs.push(parse_quote_spanned! {span=>
|
enum_attrs.push(parse_quote_spanned! {span=>
|
||||||
#[allow(dead_code, non_camel_case_types)]
|
#[allow(dead_code, non_camel_case_types)]
|
||||||
|
|
@ -483,6 +448,7 @@ impl ToTokens for ParsedEnum {
|
||||||
let mut enum_attrs = attrs.clone();
|
let mut enum_attrs = attrs.clone();
|
||||||
enum_attrs.push(parse_quote_spanned! {span=>
|
enum_attrs.push(parse_quote_spanned! {span=>
|
||||||
#[::fayalite::__std::prelude::v1::derive(
|
#[::fayalite::__std::prelude::v1::derive(
|
||||||
|
::fayalite::__std::fmt::Debug,
|
||||||
::fayalite::__std::clone::Clone,
|
::fayalite::__std::clone::Clone,
|
||||||
)]
|
)]
|
||||||
});
|
});
|
||||||
|
|
@ -583,6 +549,7 @@ impl ToTokens for ParsedEnum {
|
||||||
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
|
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
|
||||||
if let Some(ParsedVariantField { ty, .. }) = field {
|
if let Some(ParsedVariantField { ty, .. }) = field {
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
|
#[automatically_derived]
|
||||||
impl #impl_generics #target #type_generics
|
impl #impl_generics #target #type_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
|
|
@ -604,6 +571,7 @@ impl ToTokens for ParsedEnum {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[automatically_derived]
|
||||||
impl #impl_generics #sim_builder_ident #type_generics
|
impl #impl_generics #sim_builder_ident #type_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
|
|
@ -625,6 +593,7 @@ impl ToTokens for ParsedEnum {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
|
#[automatically_derived]
|
||||||
impl #impl_generics #target #type_generics
|
impl #impl_generics #target #type_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
|
|
@ -639,13 +608,12 @@ impl ToTokens for ParsedEnum {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[automatically_derived]
|
||||||
impl #impl_generics #sim_builder_ident #type_generics
|
impl #impl_generics #sim_builder_ident #type_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
#[allow(non_snake_case, dead_code)]
|
#[allow(non_snake_case, dead_code)]
|
||||||
#vis fn #ident(
|
#vis fn #ident(#self_token) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
|
||||||
#self_token,
|
|
||||||
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
|
|
||||||
::fayalite::sim::value::SimValue::from_value(
|
::fayalite::sim::value::SimValue::from_value(
|
||||||
#self_token.#sim_builder_ty_field_ident,
|
#self_token.#sim_builder_ty_field_ident,
|
||||||
#sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()),
|
#sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()),
|
||||||
|
|
@ -869,290 +837,6 @@ impl ToTokens for ParsedEnum {
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
if custom_debug_sim.is_none() {
|
|
||||||
let debug_match_arms = Vec::from_iter(
|
|
||||||
variants
|
|
||||||
.iter()
|
|
||||||
.map(
|
|
||||||
|ParsedVariant {
|
|
||||||
attrs: _,
|
|
||||||
options: _,
|
|
||||||
ident,
|
|
||||||
field,
|
|
||||||
}| {
|
|
||||||
let variant_name = ident.to_string();
|
|
||||||
if let Some(_) = field {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#sim_value_ident::#ident(field, _) => {
|
|
||||||
f.debug_tuple(#variant_name).field(field).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#sim_value_ident::#ident(_) => {
|
|
||||||
f.write_str(#variant_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.chain(sim_value_unknown_variant_name.as_ref().map(
|
|
||||||
|sim_value_unknown_variant_name| {
|
|
||||||
let sim_value_unknown_variant_name_str =
|
|
||||||
sim_value_unknown_variant_name.to_string();
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#sim_value_ident::#sim_value_unknown_variant_name(_) => {
|
|
||||||
f.write_str(#sim_value_unknown_variant_name_str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::ty::SimValueDebug for #target #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as ::fayalite::ty::Type>::SimValue,
|
|
||||||
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
|
|
||||||
) -> ::fayalite::__std::fmt::Result {
|
|
||||||
match value {
|
|
||||||
#(#debug_match_arms)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
if custom_sim_display.is_some() {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn fmt(
|
|
||||||
&self,
|
|
||||||
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
|
|
||||||
) -> ::fayalite::__std::fmt::Result {
|
|
||||||
<#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(
|
|
||||||
self,
|
|
||||||
f,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
if let Some((cmp_eq,)) = cmp_eq {
|
|
||||||
let mut cmp_eq_where_clause =
|
|
||||||
Generics::from(generics)
|
|
||||||
.where_clause
|
|
||||||
.unwrap_or_else(|| syn::WhereClause {
|
|
||||||
where_token: Token,
|
|
||||||
predicates: Punctuated::new(),
|
|
||||||
});
|
|
||||||
let mut variants_value_eq = vec![];
|
|
||||||
let mut variants_expr_eq = vec![];
|
|
||||||
let mut fields_valueless_eq = vec![];
|
|
||||||
let mut structural_eq: Option<TokenStream> = None;
|
|
||||||
for (
|
|
||||||
variant_index,
|
|
||||||
ParsedVariant {
|
|
||||||
attrs: _,
|
|
||||||
options: variant_options,
|
|
||||||
ident: variant_ident,
|
|
||||||
field,
|
|
||||||
},
|
|
||||||
) in variants.iter().enumerate()
|
|
||||||
{
|
|
||||||
let VariantOptions {} = variant_options.body;
|
|
||||||
if let Some(ParsedVariantField {
|
|
||||||
paren_token: _,
|
|
||||||
attrs: _,
|
|
||||||
options: field_options,
|
|
||||||
ty: field_ty,
|
|
||||||
comma_token: _,
|
|
||||||
}) = field
|
|
||||||
{
|
|
||||||
let FieldOptions {} = field_options.body;
|
|
||||||
cmp_eq_where_clause
|
|
||||||
.predicates
|
|
||||||
.push(parse_quote_spanned! {cmp_eq.span=>
|
|
||||||
#field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty>
|
|
||||||
});
|
|
||||||
match &mut structural_eq {
|
|
||||||
Some(structural_eq) => {
|
|
||||||
structural_eq.extend(quote_spanned! {cmp_eq.span=>
|
|
||||||
&& <#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
structural_eq = Some(quote_spanned! {cmp_eq.span=>
|
|
||||||
<#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
variants_value_eq.push(quote_spanned! {span=>
|
|
||||||
(
|
|
||||||
#sim_value_ident::#variant_ident(__lhs_field, _),
|
|
||||||
#sim_value_ident::#variant_ident(__rhs_field, _),
|
|
||||||
) => {
|
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_value_eq(
|
|
||||||
__lhs.#variant_ident,
|
|
||||||
::fayalite::__std::borrow::Cow::Borrowed(__lhs_field),
|
|
||||||
__rhs.#variant_ident,
|
|
||||||
::fayalite::__std::borrow::Cow::Borrowed(__rhs_field),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
variants_expr_eq.push(quote_spanned! {span=>
|
|
||||||
{
|
|
||||||
let (#match_variant_ident::#variant_ident(__lhs), __scope) =
|
|
||||||
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
|
|
||||||
::fayalite::__std::iter::Iterator::next(&mut __lhs_match_variant_iter)
|
|
||||||
.expect("known to have enough variants"),
|
|
||||||
)
|
|
||||||
else {
|
|
||||||
::fayalite::__std::unreachable!();
|
|
||||||
};
|
|
||||||
let (#match_variant_ident::#variant_ident(__rhs), __scope) =
|
|
||||||
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
|
|
||||||
::fayalite::__std::iter::Iterator::nth(
|
|
||||||
&mut ::fayalite::module::match_(__rhs),
|
|
||||||
#variant_index,
|
|
||||||
)
|
|
||||||
.expect("known to have variant"),
|
|
||||||
)
|
|
||||||
else {
|
|
||||||
::fayalite::__std::unreachable!();
|
|
||||||
};
|
|
||||||
::fayalite::module::connect(
|
|
||||||
__retval,
|
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(__lhs, __rhs),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fields_valueless_eq.push(quote_spanned! {span=>
|
|
||||||
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq(
|
|
||||||
::fayalite::expr::Valueless::new(__lhs.#variant_ident),
|
|
||||||
::fayalite::expr::Valueless::new(__rhs.#variant_ident),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
variants_value_eq.push(quote_spanned! {span=>
|
|
||||||
(#sim_value_ident::#variant_ident(_), #sim_value_ident::#variant_ident(_)) => true,
|
|
||||||
});
|
|
||||||
variants_expr_eq.push(quote_spanned! {span=>
|
|
||||||
{
|
|
||||||
let (#match_variant_ident::#variant_ident, __scope) =
|
|
||||||
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
|
|
||||||
::fayalite::__std::iter::Iterator::next(&mut __lhs_match_variant_iter)
|
|
||||||
.expect("known to have enough variants"),
|
|
||||||
)
|
|
||||||
else {
|
|
||||||
::fayalite::__std::unreachable!();
|
|
||||||
};
|
|
||||||
let (#match_variant_ident::#variant_ident, __scope) =
|
|
||||||
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
|
|
||||||
::fayalite::__std::iter::Iterator::nth(
|
|
||||||
&mut ::fayalite::module::match_(__rhs),
|
|
||||||
#variant_index,
|
|
||||||
)
|
|
||||||
.expect("known to have variant"),
|
|
||||||
)
|
|
||||||
else {
|
|
||||||
::fayalite::__std::unreachable!();
|
|
||||||
};
|
|
||||||
::fayalite::module::connect(__retval, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name {
|
|
||||||
variants_value_eq.push(quote_spanned! {span=>
|
|
||||||
(
|
|
||||||
#sim_value_ident::#sim_value_unknown_variant_name(__lhs_unknown),
|
|
||||||
#sim_value_ident::#sim_value_unknown_variant_name(__rhs_unknown),
|
|
||||||
) => {
|
|
||||||
__lhs_unknown == __rhs_unknown
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let valueless_eq_body = if fields_valueless_eq.is_empty() {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
|
|
||||||
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
|
|
||||||
#(#fields_valueless_eq)&*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let cmp_expr_eq_wire_name = format!("{ident}_cmp_eq");
|
|
||||||
let structural_eq = structural_eq.unwrap_or_else(|| {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::expr::HdlPartialEqImpl<Self> for #target #type_generics
|
|
||||||
#cmp_eq_where_clause
|
|
||||||
{
|
|
||||||
const TRY_STRUCTURAL_EQ: ::fayalite::__std::primitive::bool = #structural_eq;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_eq(
|
|
||||||
__lhs: Self,
|
|
||||||
__lhs_value: ::fayalite::__std::borrow::Cow<'_,
|
|
||||||
<Self as ::fayalite::ty::Type>::SimValue>,
|
|
||||||
__rhs: Self,
|
|
||||||
__rhs_value: ::fayalite::__std::borrow::Cow<'_,
|
|
||||||
<Self as ::fayalite::ty::Type>::SimValue>,
|
|
||||||
) -> ::fayalite::__std::primitive::bool {
|
|
||||||
match (&*__lhs_value, &*__rhs_value) {
|
|
||||||
#(#variants_value_eq)*
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_eq(
|
|
||||||
__lhs: ::fayalite::expr::Expr<Self>,
|
|
||||||
__rhs: ::fayalite::expr::Expr<Self>,
|
|
||||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
|
||||||
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::TRY_STRUCTURAL_EQ {
|
|
||||||
if let ::fayalite::__std::result::Result::Ok(__retval) =
|
|
||||||
::fayalite::expr::ops::StructuralEq::try_new(
|
|
||||||
::fayalite::expr::Expr::canonical(__lhs),
|
|
||||||
::fayalite::expr::Expr::canonical(__rhs),
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return ::fayalite::expr::ToExpr::to_expr(&__retval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let __retval = ::fayalite::module::wire(
|
|
||||||
::fayalite::module::ImplicitName(#cmp_expr_eq_wire_name),
|
|
||||||
::fayalite::int::Bool,
|
|
||||||
);
|
|
||||||
::fayalite::module::connect(__retval, false);
|
|
||||||
let mut __lhs_match_variant_iter = ::fayalite::module::match_(__lhs);
|
|
||||||
#(#variants_expr_eq)*
|
|
||||||
__retval
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_valueless_eq(
|
|
||||||
__lhs: ::fayalite::expr::Valueless<Self>,
|
|
||||||
__rhs: ::fayalite::expr::Valueless<Self>,
|
|
||||||
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
|
|
||||||
#valueless_eq_body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
let variants_len = variants.len();
|
let variants_len = variants.len();
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
|
|
@ -1164,8 +848,7 @@ impl ToTokens for ParsedEnum {
|
||||||
type SimValue = #sim_value_ident #type_generics;
|
type SimValue = #sim_value_ident #type_generics;
|
||||||
type MatchVariant = #match_variant_ident #type_generics;
|
type MatchVariant = #match_variant_ident #type_generics;
|
||||||
type MatchActiveScope = ::fayalite::module::Scope;
|
type MatchActiveScope = ::fayalite::module::Scope;
|
||||||
type MatchVariantAndInactiveScope =
|
type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
|
||||||
::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
|
|
||||||
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
|
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
|
||||||
|
|
||||||
fn match_variants(
|
fn match_variants(
|
||||||
|
|
@ -1178,9 +861,7 @@ impl ToTokens for ParsedEnum {
|
||||||
::fayalite::int::Bool
|
::fayalite::int::Bool
|
||||||
}
|
}
|
||||||
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType {
|
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType {
|
||||||
::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(
|
::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(#self_token)))
|
||||||
::fayalite::enum_::EnumType::variants(#self_token),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
|
@ -1189,11 +870,7 @@ impl ToTokens for ParsedEnum {
|
||||||
::fayalite::__std::panic!("expected enum");
|
::fayalite::__std::panic!("expected enum");
|
||||||
};
|
};
|
||||||
let #variants_token = ::fayalite::enum_::EnumType::variants(&enum_);
|
let #variants_token = ::fayalite::enum_::EnumType::variants(&enum_);
|
||||||
::fayalite::__std::assert_eq!(
|
::fayalite::__std::assert_eq!(#variants_token.len(), #variants_len, "enum has wrong number of variants");
|
||||||
#variants_token.len(),
|
|
||||||
#variants_len,
|
|
||||||
"enum has wrong number of variants",
|
|
||||||
);
|
|
||||||
Self {
|
Self {
|
||||||
#(#from_canonical_body_fields)*
|
#(#from_canonical_body_fields)*
|
||||||
}
|
}
|
||||||
|
|
@ -1210,7 +887,6 @@ impl ToTokens for ParsedEnum {
|
||||||
#(#sim_value_from_opaque_match_arms)*
|
#(#sim_value_from_opaque_match_arms)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(irrefutable_let_patterns)]
|
|
||||||
fn sim_value_clone_from_opaque(
|
fn sim_value_clone_from_opaque(
|
||||||
&self,
|
&self,
|
||||||
value: &mut <Self as ::fayalite::ty::Type>::SimValue,
|
value: &mut <Self as ::fayalite::ty::Type>::SimValue,
|
||||||
|
|
@ -1239,10 +915,7 @@ impl ToTokens for ParsedEnum {
|
||||||
type SimBuilder = #sim_builder_ident #type_generics;
|
type SimBuilder = #sim_builder_ident #type_generics;
|
||||||
fn match_activate_scope(
|
fn match_activate_scope(
|
||||||
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||||
) -> (
|
) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) {
|
||||||
<Self as ::fayalite::ty::Type>::MatchVariant,
|
|
||||||
<Self as ::fayalite::ty::Type>::MatchActiveScope,
|
|
||||||
) {
|
|
||||||
let (#variant_access_token, scope) = v.activate();
|
let (#variant_access_token, scope) = v.activate();
|
||||||
(
|
(
|
||||||
match #variant_access_token.variant_index() {
|
match #variant_access_token.variant_index() {
|
||||||
|
|
@ -1259,17 +932,6 @@ impl ToTokens for ParsedEnum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn fmt(
|
|
||||||
&self,
|
|
||||||
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
|
|
||||||
) -> ::fayalite::__std::fmt::Result {
|
|
||||||
<#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics>
|
impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics>
|
||||||
for #sim_value_ident #type_generics
|
for #sim_value_ident #type_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
|
|
@ -1278,10 +940,7 @@ impl ToTokens for ParsedEnum {
|
||||||
&self,
|
&self,
|
||||||
ty: #target #type_generics,
|
ty: #target #type_generics,
|
||||||
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
|
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
|
||||||
::fayalite::sim::value::SimValue::from_value(
|
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
|
||||||
ty,
|
|
||||||
::fayalite::__std::clone::Clone::clone(self),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
fn into_sim_value_with_type(
|
fn into_sim_value_with_type(
|
||||||
self,
|
self,
|
||||||
|
|
@ -1364,26 +1023,16 @@ impl ToTokens for ParsedEnum {
|
||||||
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
|
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
|
||||||
}
|
}
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #static_impl_generics ::fayalite::expr::ValueType
|
|
||||||
for #sim_value_ident #static_type_generics
|
|
||||||
#static_where_clause
|
|
||||||
{
|
|
||||||
type Type = #target #static_type_generics;
|
|
||||||
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
|
|
||||||
|
|
||||||
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
|
|
||||||
::fayalite::ty::StaticType::TYPE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #static_impl_generics ::fayalite::sim::value::ToSimValue
|
impl #static_impl_generics ::fayalite::sim::value::ToSimValue
|
||||||
for #sim_value_ident #static_type_generics
|
for #sim_value_ident #static_type_generics
|
||||||
#static_where_clause
|
#static_where_clause
|
||||||
{
|
{
|
||||||
|
type Type = #target #static_type_generics;
|
||||||
|
|
||||||
fn to_sim_value(
|
fn to_sim_value(
|
||||||
&self,
|
&self,
|
||||||
) -> ::fayalite::sim::value::SimValue<
|
) -> ::fayalite::sim::value::SimValue<
|
||||||
<Self as ::fayalite::expr::ValueType>::Type,
|
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||||
> {
|
> {
|
||||||
::fayalite::sim::value::SimValue::from_value(
|
::fayalite::sim::value::SimValue::from_value(
|
||||||
::fayalite::ty::StaticType::TYPE,
|
::fayalite::ty::StaticType::TYPE,
|
||||||
|
|
@ -1393,7 +1042,7 @@ impl ToTokens for ParsedEnum {
|
||||||
fn into_sim_value(
|
fn into_sim_value(
|
||||||
self,
|
self,
|
||||||
) -> ::fayalite::sim::value::SimValue<
|
) -> ::fayalite::sim::value::SimValue<
|
||||||
<Self as ::fayalite::expr::ValueType>::Type,
|
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||||
> {
|
> {
|
||||||
::fayalite::sim::value::SimValue::from_value(
|
::fayalite::sim::value::SimValue::from_value(
|
||||||
::fayalite::ty::StaticType::TYPE,
|
::fayalite::ty::StaticType::TYPE,
|
||||||
|
|
|
||||||
|
|
@ -3,275 +3,29 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Errors, HdlAttr,
|
Errors, HdlAttr,
|
||||||
hdl_type_common::{
|
hdl_type_common::{
|
||||||
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType,
|
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, TypesParser,
|
||||||
PhantomConstGetBound, TypesParser, WrappedInConst, common_derives, get_target, known_items,
|
get_target,
|
||||||
},
|
},
|
||||||
kw,
|
kw,
|
||||||
};
|
};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{ToTokens, format_ident, quote_spanned};
|
use quote::ToTokens;
|
||||||
use syn::{
|
use syn::{Attribute, Generics, Ident, ItemType, Token, Type, Visibility, parse_quote_spanned};
|
||||||
Attribute, Expr, Fields, GenericParam, Generics, Ident, ItemStruct, ItemType, Token, Type,
|
|
||||||
TypeGroup, TypeParam, TypeParen, Visibility, parse_quote_spanned, punctuated::Pair,
|
|
||||||
token::Paren,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct PhantomConstAccessorTypeParam {
|
pub(crate) struct ParsedTypeAlias {
|
||||||
attrs: Vec<Attribute>,
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
ident: Ident,
|
pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
|
||||||
colon_token: Token![:],
|
pub(crate) vis: Visibility,
|
||||||
phantom_const_get_bound: PhantomConstGetBound,
|
pub(crate) type_token: Token![type],
|
||||||
plus_token: Option<Token![+]>,
|
pub(crate) ident: Ident,
|
||||||
}
|
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
|
||||||
|
pub(crate) eq_token: Token![=],
|
||||||
impl From<PhantomConstAccessorTypeParam> for TypeParam {
|
pub(crate) ty: MaybeParsed<ParsedType, Type>,
|
||||||
fn from(value: PhantomConstAccessorTypeParam) -> Self {
|
pub(crate) semi_token: Token![;],
|
||||||
let PhantomConstAccessorTypeParam {
|
|
||||||
attrs,
|
|
||||||
ident,
|
|
||||||
colon_token,
|
|
||||||
phantom_const_get_bound,
|
|
||||||
plus_token,
|
|
||||||
} = value;
|
|
||||||
TypeParam {
|
|
||||||
attrs,
|
|
||||||
ident,
|
|
||||||
colon_token: Some(colon_token),
|
|
||||||
bounds: FromIterator::from_iter([Pair::new(
|
|
||||||
phantom_const_get_bound.into(),
|
|
||||||
plus_token,
|
|
||||||
)]),
|
|
||||||
eq_token: None,
|
|
||||||
default: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PhantomConstAccessorTypeParam> for GenericParam {
|
|
||||||
fn from(value: PhantomConstAccessorTypeParam) -> Self {
|
|
||||||
TypeParam::from(value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhantomConstAccessorTypeParam {
|
|
||||||
fn parse_opt(generic_param: GenericParam) -> Option<Self> {
|
|
||||||
let GenericParam::Type(TypeParam {
|
|
||||||
attrs,
|
|
||||||
ident,
|
|
||||||
colon_token,
|
|
||||||
bounds,
|
|
||||||
eq_token: None,
|
|
||||||
default: None,
|
|
||||||
}) = generic_param
|
|
||||||
else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let colon_token = colon_token.unwrap_or(Token));
|
|
||||||
let mut bounds = bounds.into_pairs();
|
|
||||||
let (bound, plus_token) = bounds.next()?.into_tuple();
|
|
||||||
let phantom_const_get_bound = PhantomConstGetBound::parse_type_param_bound(bound)
|
|
||||||
.ok()?
|
|
||||||
.ok()?;
|
|
||||||
let None = bounds.next() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
Some(Self {
|
|
||||||
attrs,
|
|
||||||
ident,
|
|
||||||
colon_token,
|
|
||||||
phantom_const_get_bound,
|
|
||||||
plus_token,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub(crate) struct PhantomConstAccessorGenerics {
|
|
||||||
lt_token: Token![<],
|
|
||||||
type_param: PhantomConstAccessorTypeParam,
|
|
||||||
comma_token: Option<Token![,]>,
|
|
||||||
gt_token: Token![>],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PhantomConstAccessorGenerics> for Generics {
|
|
||||||
fn from(value: PhantomConstAccessorGenerics) -> Self {
|
|
||||||
let PhantomConstAccessorGenerics {
|
|
||||||
lt_token,
|
|
||||||
type_param,
|
|
||||||
comma_token,
|
|
||||||
gt_token,
|
|
||||||
} = value;
|
|
||||||
Generics {
|
|
||||||
lt_token: Some(lt_token),
|
|
||||||
params: FromIterator::from_iter([Pair::new(type_param.into(), comma_token)]),
|
|
||||||
gt_token: Some(gt_token),
|
|
||||||
where_clause: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a PhantomConstAccessorGenerics> for Generics {
|
|
||||||
fn from(value: &'a PhantomConstAccessorGenerics) -> Self {
|
|
||||||
value.clone().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhantomConstAccessorGenerics {
|
|
||||||
fn parse_opt(generics: Generics) -> Option<Self> {
|
|
||||||
let Generics {
|
|
||||||
lt_token,
|
|
||||||
params,
|
|
||||||
gt_token,
|
|
||||||
where_clause: None,
|
|
||||||
} = generics
|
|
||||||
else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let mut params = params.into_pairs();
|
|
||||||
let (generic_param, comma_token) = params.next()?.into_tuple();
|
|
||||||
let type_param = PhantomConstAccessorTypeParam::parse_opt(generic_param)?;
|
|
||||||
let span = type_param.ident.span();
|
|
||||||
let lt_token = lt_token.unwrap_or(Token);
|
|
||||||
let gt_token = gt_token.unwrap_or(Token);
|
|
||||||
let None = params.next() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
Some(Self {
|
|
||||||
lt_token,
|
|
||||||
type_param,
|
|
||||||
comma_token,
|
|
||||||
gt_token,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub(crate) enum ParsedTypeAlias {
|
|
||||||
TypeAlias {
|
|
||||||
attrs: Vec<Attribute>,
|
|
||||||
options: HdlAttr<ItemOptions, kw::hdl>,
|
|
||||||
vis: Visibility,
|
|
||||||
type_token: Token![type],
|
|
||||||
ident: Ident,
|
|
||||||
generics: MaybeParsed<ParsedGenerics, Generics>,
|
|
||||||
eq_token: Token![=],
|
|
||||||
ty: MaybeParsed<ParsedType, Type>,
|
|
||||||
semi_token: Token![;],
|
|
||||||
},
|
|
||||||
PhantomConstAccessor {
|
|
||||||
attrs: Vec<Attribute>,
|
|
||||||
options: HdlAttr<ItemOptions, kw::hdl>,
|
|
||||||
get: (kw::get, Paren, Expr),
|
|
||||||
vis: Visibility,
|
|
||||||
type_token: Token![type],
|
|
||||||
ident: Ident,
|
|
||||||
generics: PhantomConstAccessorGenerics,
|
|
||||||
eq_token: Token![=],
|
|
||||||
ty: Type,
|
|
||||||
ty_is_dyn_size: Option<known_items::DynSize>,
|
|
||||||
semi_token: Token![;],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParsedTypeAlias {
|
impl ParsedTypeAlias {
|
||||||
fn ty_is_dyn_size(ty: &Type) -> Option<known_items::DynSize> {
|
|
||||||
match ty {
|
|
||||||
Type::Group(TypeGroup {
|
|
||||||
group_token: _,
|
|
||||||
elem,
|
|
||||||
}) => Self::ty_is_dyn_size(elem),
|
|
||||||
Type::Paren(TypeParen {
|
|
||||||
paren_token: _,
|
|
||||||
elem,
|
|
||||||
}) => Self::ty_is_dyn_size(elem),
|
|
||||||
Type::Path(syn::TypePath { qself: None, path }) => {
|
|
||||||
known_items::DynSize::parse_path(path.clone()).ok()
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn parse_phantom_const_accessor(
|
|
||||||
item: ItemType,
|
|
||||||
mut errors: Errors,
|
|
||||||
options: HdlAttr<ItemOptions, kw::hdl>,
|
|
||||||
get: (kw::get, Paren, Expr),
|
|
||||||
) -> syn::Result<Self> {
|
|
||||||
let ItemType {
|
|
||||||
attrs,
|
|
||||||
vis,
|
|
||||||
type_token,
|
|
||||||
ident,
|
|
||||||
generics,
|
|
||||||
eq_token,
|
|
||||||
ty,
|
|
||||||
semi_token,
|
|
||||||
} = item;
|
|
||||||
let ItemOptions {
|
|
||||||
outline_generated: _,
|
|
||||||
ref target,
|
|
||||||
custom_bounds,
|
|
||||||
no_static,
|
|
||||||
no_runtime_generics,
|
|
||||||
cmp_eq,
|
|
||||||
get: _,
|
|
||||||
ref custom_debug,
|
|
||||||
custom_sim_display,
|
|
||||||
} = options.body;
|
|
||||||
if let Some((no_static,)) = no_static {
|
|
||||||
errors.error(no_static, "no_static is not valid on type aliases");
|
|
||||||
}
|
|
||||||
if let Some((target, ..)) = target {
|
|
||||||
errors.error(
|
|
||||||
target,
|
|
||||||
"target is not implemented on PhantomConstGet type aliases",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some((no_runtime_generics,)) = no_runtime_generics {
|
|
||||||
errors.error(
|
|
||||||
no_runtime_generics,
|
|
||||||
"no_runtime_generics is not implemented on PhantomConstGet type aliases",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some((cmp_eq,)) = cmp_eq {
|
|
||||||
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
|
|
||||||
}
|
|
||||||
if let Some((custom_debug, _, _)) = custom_debug {
|
|
||||||
errors.error(custom_debug, "custom_debug is not valid on type aliases");
|
|
||||||
}
|
|
||||||
if let Some((custom_sim_display,)) = custom_sim_display {
|
|
||||||
errors.error(
|
|
||||||
custom_sim_display,
|
|
||||||
"custom_sim_display is not valid on type aliases",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some((custom_bounds,)) = custom_bounds {
|
|
||||||
errors.error(
|
|
||||||
custom_bounds,
|
|
||||||
"custom_bounds is not implemented on PhantomConstGet type aliases",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let Some(generics) = PhantomConstAccessorGenerics::parse_opt(generics) else {
|
|
||||||
errors.error(ident, "#[hdl(get(...))] type alias must be of the form:\ntype MyTypeGetter<P: PhantomConstGet<MyType>> = RetType;");
|
|
||||||
errors.finish()?;
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
errors.finish()?;
|
|
||||||
let ty_is_dyn_size = Self::ty_is_dyn_size(&ty);
|
|
||||||
Ok(Self::PhantomConstAccessor {
|
|
||||||
attrs,
|
|
||||||
options,
|
|
||||||
get,
|
|
||||||
vis,
|
|
||||||
type_token,
|
|
||||||
ident,
|
|
||||||
generics,
|
|
||||||
eq_token,
|
|
||||||
ty: *ty,
|
|
||||||
ty_is_dyn_size,
|
|
||||||
semi_token,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn parse(item: ItemType) -> syn::Result<Self> {
|
fn parse(item: ItemType) -> syn::Result<Self> {
|
||||||
let ItemType {
|
let ItemType {
|
||||||
mut attrs,
|
mut attrs,
|
||||||
|
|
@ -297,42 +51,13 @@ impl ParsedTypeAlias {
|
||||||
no_static,
|
no_static,
|
||||||
no_runtime_generics: _,
|
no_runtime_generics: _,
|
||||||
cmp_eq,
|
cmp_eq,
|
||||||
ref mut get,
|
|
||||||
ref custom_debug,
|
|
||||||
custom_sim_display,
|
|
||||||
} = options.body;
|
} = options.body;
|
||||||
if let Some(get) = get.take() {
|
|
||||||
return Self::parse_phantom_const_accessor(
|
|
||||||
ItemType {
|
|
||||||
attrs,
|
|
||||||
vis,
|
|
||||||
type_token,
|
|
||||||
ident,
|
|
||||||
generics,
|
|
||||||
eq_token,
|
|
||||||
ty,
|
|
||||||
semi_token,
|
|
||||||
},
|
|
||||||
errors,
|
|
||||||
options,
|
|
||||||
get,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some((no_static,)) = no_static {
|
if let Some((no_static,)) = no_static {
|
||||||
errors.error(no_static, "no_static is not valid on type aliases");
|
errors.error(no_static, "no_static is not valid on type aliases");
|
||||||
}
|
}
|
||||||
if let Some((cmp_eq,)) = cmp_eq {
|
if let Some((cmp_eq,)) = cmp_eq {
|
||||||
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
|
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
|
||||||
}
|
}
|
||||||
if let Some((custom_debug, _, _)) = custom_debug {
|
|
||||||
errors.error(custom_debug, "custom_debug is not valid on type aliases");
|
|
||||||
}
|
|
||||||
if let Some((custom_sim_display,)) = custom_sim_display {
|
|
||||||
errors.error(
|
|
||||||
custom_sim_display,
|
|
||||||
"custom_sim_display is not valid on type aliases",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let generics = if custom_bounds.is_some() {
|
let generics = if custom_bounds.is_some() {
|
||||||
MaybeParsed::Unrecognized(generics)
|
MaybeParsed::Unrecognized(generics)
|
||||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||||
|
|
@ -342,7 +67,7 @@ impl ParsedTypeAlias {
|
||||||
};
|
};
|
||||||
let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors);
|
let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors);
|
||||||
errors.finish()?;
|
errors.finish()?;
|
||||||
Ok(Self::TypeAlias {
|
Ok(Self {
|
||||||
attrs,
|
attrs,
|
||||||
options,
|
options,
|
||||||
vis,
|
vis,
|
||||||
|
|
@ -358,8 +83,7 @@ impl ParsedTypeAlias {
|
||||||
|
|
||||||
impl ToTokens for ParsedTypeAlias {
|
impl ToTokens for ParsedTypeAlias {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
match self {
|
let Self {
|
||||||
Self::TypeAlias {
|
|
||||||
attrs,
|
attrs,
|
||||||
options,
|
options,
|
||||||
vis,
|
vis,
|
||||||
|
|
@ -369,7 +93,7 @@ impl ToTokens for ParsedTypeAlias {
|
||||||
eq_token,
|
eq_token,
|
||||||
ty,
|
ty,
|
||||||
semi_token,
|
semi_token,
|
||||||
} => {
|
} = self;
|
||||||
let ItemOptions {
|
let ItemOptions {
|
||||||
outline_generated: _,
|
outline_generated: _,
|
||||||
target,
|
target,
|
||||||
|
|
@ -377,9 +101,6 @@ impl ToTokens for ParsedTypeAlias {
|
||||||
no_static: _,
|
no_static: _,
|
||||||
no_runtime_generics,
|
no_runtime_generics,
|
||||||
cmp_eq: _,
|
cmp_eq: _,
|
||||||
get: _,
|
|
||||||
custom_debug: _,
|
|
||||||
custom_sim_display: _,
|
|
||||||
} = &options.body;
|
} = &options.body;
|
||||||
let target = get_target(target, ident);
|
let target = get_target(target, ident);
|
||||||
let mut type_attrs = attrs.clone();
|
let mut type_attrs = attrs.clone();
|
||||||
|
|
@ -405,112 +126,11 @@ impl ToTokens for ParsedTypeAlias {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::PhantomConstAccessor {
|
|
||||||
attrs,
|
|
||||||
options,
|
|
||||||
get: (_get_kw, _get_paren, get_expr),
|
|
||||||
vis,
|
|
||||||
type_token,
|
|
||||||
ident,
|
|
||||||
generics,
|
|
||||||
eq_token,
|
|
||||||
ty,
|
|
||||||
ty_is_dyn_size,
|
|
||||||
semi_token,
|
|
||||||
} => {
|
|
||||||
let ItemOptions {
|
|
||||||
outline_generated: _,
|
|
||||||
target: _,
|
|
||||||
custom_bounds: _,
|
|
||||||
no_static: _,
|
|
||||||
no_runtime_generics: _,
|
|
||||||
cmp_eq: _,
|
|
||||||
get: _,
|
|
||||||
custom_debug: _,
|
|
||||||
custom_sim_display: _,
|
|
||||||
} = &options.body;
|
|
||||||
let span = ident.span();
|
|
||||||
let mut type_attrs = attrs.clone();
|
|
||||||
type_attrs.push(parse_quote_spanned! {span=>
|
|
||||||
#[allow(type_alias_bounds)]
|
|
||||||
});
|
|
||||||
let type_param_ident = &generics.type_param.ident;
|
|
||||||
let syn_generics = Generics::from(generics);
|
|
||||||
ItemType {
|
|
||||||
attrs: type_attrs,
|
|
||||||
vis: vis.clone(),
|
|
||||||
type_token: *type_token,
|
|
||||||
ident: ident.clone(),
|
|
||||||
generics: syn_generics.clone(),
|
|
||||||
eq_token: *eq_token,
|
|
||||||
ty: parse_quote_spanned! {span=>
|
|
||||||
<#ty as ::fayalite::phantom_const::ReturnSelfUnchanged<#type_param_ident>>::Type
|
|
||||||
},
|
|
||||||
semi_token: *semi_token,
|
|
||||||
}
|
|
||||||
.to_tokens(tokens);
|
|
||||||
let generics_accumulation_ident =
|
|
||||||
format_ident!("__{}__GenericsAccumulation", ident);
|
|
||||||
ItemStruct {
|
|
||||||
attrs: vec![
|
|
||||||
common_derives(span, true),
|
|
||||||
parse_quote_spanned! {span=>
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
},
|
|
||||||
],
|
|
||||||
vis: vis.clone(),
|
|
||||||
struct_token: Token,
|
|
||||||
ident: generics_accumulation_ident.clone(),
|
|
||||||
generics: Generics::default(),
|
|
||||||
fields: Fields::Unnamed(parse_quote_spanned! {span=>
|
|
||||||
(())
|
|
||||||
}),
|
|
||||||
semi_token: Some(Token),
|
|
||||||
}
|
|
||||||
.to_tokens(tokens);
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#[allow(non_upper_case_globals, dead_code)]
|
|
||||||
#vis const #ident: #generics_accumulation_ident = #generics_accumulation_ident(());
|
|
||||||
}
|
|
||||||
.to_tokens(tokens);
|
|
||||||
let mut wrapped_in_const = WrappedInConst::new(tokens, span);
|
|
||||||
let tokens = wrapped_in_const.inner();
|
|
||||||
let (impl_generics, _type_generics, where_clause) = syn_generics.split_for_impl();
|
|
||||||
let phantom_const_get_ty = &generics.type_param.phantom_const_get_bound.ty;
|
|
||||||
let index_output = if let Some(ty_is_dyn_size) = ty_is_dyn_size {
|
|
||||||
known_items::usize(ty_is_dyn_size.span).to_token_stream()
|
|
||||||
} else {
|
|
||||||
ty.to_token_stream()
|
|
||||||
};
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics ::fayalite::__std::ops::Index<#type_param_ident>
|
|
||||||
for #generics_accumulation_ident
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
type Output = #index_output;
|
|
||||||
|
|
||||||
fn index(&self, __param: #type_param_ident) -> &Self::Output {
|
|
||||||
::fayalite::phantom_const::type_alias_phantom_const_get_helper::<#phantom_const_get_ty, #index_output>(
|
|
||||||
__param,
|
|
||||||
#get_expr,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result<TokenStream> {
|
pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result<TokenStream> {
|
||||||
let item = ParsedTypeAlias::parse(item)?;
|
let item = ParsedTypeAlias::parse(item)?;
|
||||||
let outline_generated = match &item {
|
let outline_generated = item.options.body.outline_generated;
|
||||||
ParsedTypeAlias::TypeAlias { options, .. }
|
|
||||||
| ParsedTypeAlias::PhantomConstAccessor { options, .. } => options.body.outline_generated,
|
|
||||||
};
|
|
||||||
let mut contents = item.to_token_stream();
|
let mut contents = item.to_token_stream();
|
||||||
if outline_generated.is_some() {
|
if outline_generated.is_some() {
|
||||||
contents = crate::outline_generated(contents, "hdl-type-alias-");
|
contents = crate::outline_generated(contents, "hdl-type-alias-");
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ use std::{collections::HashMap, fmt, mem};
|
||||||
use syn::{
|
use syn::{
|
||||||
AngleBracketedGenericArguments, Attribute, Block, ConstParam, Expr, ExprBlock, ExprGroup,
|
AngleBracketedGenericArguments, Attribute, Block, ConstParam, Expr, ExprBlock, ExprGroup,
|
||||||
ExprIndex, ExprParen, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed,
|
ExprIndex, ExprParen, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed,
|
||||||
FieldsUnnamed, FnArg, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index,
|
FieldsUnnamed, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, ItemStruct,
|
||||||
ItemStruct, Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, TraitBound,
|
Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, Turbofish, Type,
|
||||||
Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, TypeParamBound, TypeParen, TypePath,
|
TypeGenerics, TypeGroup, TypeParam, TypeParen, TypePath, TypeTuple, Visibility, WhereClause,
|
||||||
TypeTuple, Visibility, WhereClause, WherePredicate,
|
WherePredicate,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
parse_quote, parse_quote_spanned,
|
parse_quote, parse_quote_spanned,
|
||||||
punctuated::{Pair, Punctuated},
|
punctuated::{Pair, Punctuated},
|
||||||
|
|
@ -18,17 +18,6 @@ use syn::{
|
||||||
token::{Brace, Bracket, Paren},
|
token::{Brace, Bracket, Paren},
|
||||||
};
|
};
|
||||||
|
|
||||||
crate::options! {
|
|
||||||
#[options = CustomDebugOptions]
|
|
||||||
#[no_ident_fragment]
|
|
||||||
pub(crate) enum CustomDebugOption {
|
|
||||||
Type(type_),
|
|
||||||
Sim(sim),
|
|
||||||
MaskType(mask_type),
|
|
||||||
MaskSim(mask_sim),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::options! {
|
crate::options! {
|
||||||
#[options = ItemOptions]
|
#[options = ItemOptions]
|
||||||
pub(crate) enum ItemOption {
|
pub(crate) enum ItemOption {
|
||||||
|
|
@ -38,9 +27,6 @@ crate::options! {
|
||||||
NoStatic(no_static),
|
NoStatic(no_static),
|
||||||
NoRuntimeGenerics(no_runtime_generics),
|
NoRuntimeGenerics(no_runtime_generics),
|
||||||
CmpEq(cmp_eq),
|
CmpEq(cmp_eq),
|
||||||
Get(get, Expr),
|
|
||||||
CustomDebug(custom_debug, CustomDebugOptions),
|
|
||||||
CustomSimDisplay(custom_sim_display),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,36 +40,8 @@ impl ItemOptions {
|
||||||
{
|
{
|
||||||
self.no_static = Some((kw::no_static(custom_bounds.span),));
|
self.no_static = Some((kw::no_static(custom_bounds.span),));
|
||||||
}
|
}
|
||||||
if let Some((kw, _, custom_debug)) = &mut self.custom_debug {
|
|
||||||
if let CustomDebugOptions {
|
|
||||||
type_: None,
|
|
||||||
sim: None,
|
|
||||||
mask_type: None,
|
|
||||||
mask_sim: None,
|
|
||||||
} = custom_debug
|
|
||||||
{
|
|
||||||
*custom_debug = CustomDebugOptions {
|
|
||||||
type_: Some((kw::type_(kw.span),)),
|
|
||||||
sim: Some((kw::sim(kw.span),)),
|
|
||||||
mask_type: None,
|
|
||||||
mask_sim: None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub(crate) fn custom_debug(&self) -> &CustomDebugOptions {
|
|
||||||
self.custom_debug.as_ref().map(|v| &v.2).unwrap_or(
|
|
||||||
const {
|
|
||||||
&CustomDebugOptions {
|
|
||||||
type_: None,
|
|
||||||
sim: None,
|
|
||||||
mask_type: None,
|
|
||||||
mask_sim: None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct WrappedInConst<'a> {
|
pub(crate) struct WrappedInConst<'a> {
|
||||||
|
|
@ -125,17 +83,10 @@ pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn common_derives(span: Span, include_debug: bool) -> Attribute {
|
pub(crate) fn common_derives(span: Span) -> Attribute {
|
||||||
let debug = include_debug
|
|
||||||
.then(|| {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
::fayalite::__std::fmt::Debug
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into_iter();
|
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
#[::fayalite::__std::prelude::v1::derive(
|
#[::fayalite::__std::prelude::v1::derive(
|
||||||
#(#debug,)*
|
::fayalite::__std::fmt::Debug,
|
||||||
::fayalite::__std::cmp::Eq,
|
::fayalite::__std::cmp::Eq,
|
||||||
::fayalite::__std::cmp::PartialEq,
|
::fayalite::__std::cmp::PartialEq,
|
||||||
::fayalite::__std::hash::Hash,
|
::fayalite::__std::hash::Hash,
|
||||||
|
|
@ -2094,7 +2045,6 @@ pub(crate) mod known_items {
|
||||||
impl_known_item!(::fayalite::int::Size);
|
impl_known_item!(::fayalite::int::Size);
|
||||||
impl_known_item!(::fayalite::int::UInt);
|
impl_known_item!(::fayalite::int::UInt);
|
||||||
impl_known_item!(::fayalite::int::UIntType);
|
impl_known_item!(::fayalite::int::UIntType);
|
||||||
impl_known_item!(::fayalite::phantom_const::PhantomConstGet);
|
|
||||||
impl_known_item!(::fayalite::reset::ResetType);
|
impl_known_item!(::fayalite::reset::ResetType);
|
||||||
impl_known_item!(::fayalite::ty::CanonicalType);
|
impl_known_item!(::fayalite::ty::CanonicalType);
|
||||||
impl_known_item!(::fayalite::ty::StaticType);
|
impl_known_item!(::fayalite::ty::StaticType);
|
||||||
|
|
@ -2113,174 +2063,6 @@ pub(crate) mod known_items {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub(crate) struct PhantomConstGetBound {
|
|
||||||
pub(crate) phantom_const_get: known_items::PhantomConstGet,
|
|
||||||
pub(crate) colon2_token: Option<Token![::]>,
|
|
||||||
pub(crate) lt_token: Token![<],
|
|
||||||
pub(crate) ty: Type,
|
|
||||||
pub(crate) comma_token: Option<Token![,]>,
|
|
||||||
pub(crate) gt_token: Token![>],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhantomConstGetBound {
|
|
||||||
pub(crate) fn parse_path_with_arguments(path: Path) -> syn::Result<Result<Self, Path>> {
|
|
||||||
match known_items::PhantomConstGet::parse_path_with_arguments(path) {
|
|
||||||
Ok((phantom_const_get, arguments)) => {
|
|
||||||
Self::parse_path_and_arguments(phantom_const_get, arguments).map(Ok)
|
|
||||||
}
|
|
||||||
Err(path) => Ok(Err(path)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn parse_path_and_arguments(
|
|
||||||
phantom_const_get: known_items::PhantomConstGet,
|
|
||||||
arguments: PathArguments,
|
|
||||||
) -> syn::Result<Self> {
|
|
||||||
let error = |arguments: PathArguments, message: &str| {
|
|
||||||
let mut path = phantom_const_get.path.clone();
|
|
||||||
path.segments.last_mut().expect("known to exist").arguments = arguments;
|
|
||||||
syn::Error::new_spanned(path, message)
|
|
||||||
};
|
|
||||||
match arguments {
|
|
||||||
PathArguments::None => Err(error(arguments, "missing generics for PhantomConstGet")),
|
|
||||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
|
||||||
colon2_token,
|
|
||||||
lt_token,
|
|
||||||
args,
|
|
||||||
gt_token,
|
|
||||||
}) => {
|
|
||||||
let error = |args: Punctuated<GenericArgument, Token![,]>, message| {
|
|
||||||
error(
|
|
||||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
|
||||||
colon2_token,
|
|
||||||
lt_token,
|
|
||||||
args,
|
|
||||||
gt_token,
|
|
||||||
}),
|
|
||||||
message,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let mut args = args.into_pairs().peekable();
|
|
||||||
let Some((generic_argument, comma_token)) = args.next().map(Pair::into_tuple)
|
|
||||||
else {
|
|
||||||
return Err(error(
|
|
||||||
Default::default(),
|
|
||||||
"PhantomConstGet takes a type argument but no generic arguments were supplied",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
if args.peek().is_some() {
|
|
||||||
return Err(error(
|
|
||||||
[Pair::new(generic_argument, comma_token)]
|
|
||||||
.into_iter()
|
|
||||||
.chain(args)
|
|
||||||
.collect(),
|
|
||||||
"PhantomConstGet takes a single type argument but too many generic arguments were supplied",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let GenericArgument::Type(ty) = generic_argument else {
|
|
||||||
return Err(error(
|
|
||||||
Punctuated::from_iter([Pair::new(generic_argument, comma_token)]),
|
|
||||||
"PhantomConstGet requires a type argument",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
Ok(Self {
|
|
||||||
phantom_const_get,
|
|
||||||
colon2_token,
|
|
||||||
lt_token,
|
|
||||||
ty,
|
|
||||||
comma_token,
|
|
||||||
gt_token,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
PathArguments::Parenthesized(_) => Err(error(
|
|
||||||
arguments,
|
|
||||||
"parenthetical generics are not valid for PhantomConstGet",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn parse_type_param_bound(
|
|
||||||
bound: TypeParamBound,
|
|
||||||
) -> syn::Result<Result<Self, TypeParamBound>> {
|
|
||||||
let TypeParamBound::Trait(TraitBound {
|
|
||||||
paren_token: None,
|
|
||||||
modifier: syn::TraitBoundModifier::None,
|
|
||||||
lifetimes: None,
|
|
||||||
path,
|
|
||||||
}) = bound
|
|
||||||
else {
|
|
||||||
return Ok(Err(bound));
|
|
||||||
};
|
|
||||||
Ok(match Self::parse_path_with_arguments(path)? {
|
|
||||||
Ok(v) => Ok(v),
|
|
||||||
Err(path) => Err(TypeParamBound::Trait(TraitBound {
|
|
||||||
paren_token: None,
|
|
||||||
modifier: syn::TraitBoundModifier::None,
|
|
||||||
lifetimes: None,
|
|
||||||
path,
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for PhantomConstGetBound {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let Self {
|
|
||||||
phantom_const_get,
|
|
||||||
colon2_token,
|
|
||||||
lt_token,
|
|
||||||
ty,
|
|
||||||
comma_token,
|
|
||||||
gt_token,
|
|
||||||
} = self;
|
|
||||||
phantom_const_get.to_tokens(tokens);
|
|
||||||
colon2_token.to_tokens(tokens);
|
|
||||||
lt_token.to_tokens(tokens);
|
|
||||||
ty.to_tokens(tokens);
|
|
||||||
comma_token.to_tokens(tokens);
|
|
||||||
gt_token.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PhantomConstGetBound> for Path {
|
|
||||||
fn from(value: PhantomConstGetBound) -> Self {
|
|
||||||
let PhantomConstGetBound {
|
|
||||||
phantom_const_get,
|
|
||||||
colon2_token,
|
|
||||||
lt_token,
|
|
||||||
ty,
|
|
||||||
comma_token,
|
|
||||||
gt_token,
|
|
||||||
} = value;
|
|
||||||
let mut path = phantom_const_get.path;
|
|
||||||
path.segments.last_mut().expect("known to exist").arguments =
|
|
||||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
|
||||||
colon2_token,
|
|
||||||
lt_token,
|
|
||||||
args: FromIterator::from_iter([Pair::new(GenericArgument::Type(ty), comma_token)]),
|
|
||||||
gt_token,
|
|
||||||
});
|
|
||||||
path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PhantomConstGetBound> for TraitBound {
|
|
||||||
fn from(value: PhantomConstGetBound) -> Self {
|
|
||||||
let path = Path::from(value);
|
|
||||||
TraitBound {
|
|
||||||
paren_token: None,
|
|
||||||
modifier: syn::TraitBoundModifier::None,
|
|
||||||
lifetimes: None,
|
|
||||||
path,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PhantomConstGetBound> for TypeParamBound {
|
|
||||||
fn from(value: PhantomConstGetBound) -> Self {
|
|
||||||
TraitBound::from(value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_bounds {
|
macro_rules! impl_bounds {
|
||||||
(
|
(
|
||||||
#[struct = $struct_type:ident]
|
#[struct = $struct_type:ident]
|
||||||
|
|
@ -2288,10 +2070,6 @@ macro_rules! impl_bounds {
|
||||||
$(
|
$(
|
||||||
$Variant:ident,
|
$Variant:ident,
|
||||||
)*
|
)*
|
||||||
$(
|
|
||||||
#[has_body]
|
|
||||||
$VariantHasBody:ident($variant_has_body_ty:ty),
|
|
||||||
)*
|
|
||||||
$(
|
$(
|
||||||
#[unknown]
|
#[unknown]
|
||||||
$Unknown:ident,
|
$Unknown:ident,
|
||||||
|
|
@ -2301,7 +2079,6 @@ macro_rules! impl_bounds {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
$vis enum $enum_type {
|
$vis enum $enum_type {
|
||||||
$($Variant(known_items::$Variant),)*
|
$($Variant(known_items::$Variant),)*
|
||||||
$($VariantHasBody($variant_has_body_ty),)*
|
|
||||||
$($Unknown(syn::TypeParamBound),)?
|
$($Unknown(syn::TypeParamBound),)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2311,42 +2088,31 @@ macro_rules! impl_bounds {
|
||||||
}
|
}
|
||||||
})*
|
})*
|
||||||
|
|
||||||
$(impl From<$variant_has_body_ty> for $enum_type {
|
|
||||||
fn from(v: $variant_has_body_ty) -> Self {
|
|
||||||
Self::$VariantHasBody(v)
|
|
||||||
}
|
|
||||||
})*
|
|
||||||
|
|
||||||
impl ToTokens for $enum_type {
|
impl ToTokens for $enum_type {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
match self {
|
match self {
|
||||||
$(Self::$Variant(v) => v.to_tokens(tokens),)*
|
$(Self::$Variant(v) => v.to_tokens(tokens),)*
|
||||||
$(Self::$VariantHasBody(v) => v.to_tokens(tokens),)*
|
|
||||||
$(Self::$Unknown(v) => v.to_tokens(tokens),)?
|
$(Self::$Unknown(v) => v.to_tokens(tokens),)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $enum_type {
|
impl $enum_type {
|
||||||
$vis fn parse_path_with_arguments(path: Path) -> syn::Result<Result<Self, Path>> {
|
$vis fn parse_path(path: Path) -> Result<Self, Path> {
|
||||||
#![allow(unreachable_code)]
|
#![allow(unreachable_code)]
|
||||||
$(let path = match known_items::$Variant::parse_path(path) {
|
$(let path = match known_items::$Variant::parse_path(path) {
|
||||||
Ok(v) => return Ok(Ok(Self::$Variant(v))),
|
Ok(v) => return Ok(Self::$Variant(v)),
|
||||||
Err(path) => path,
|
Err(path) => path,
|
||||||
};)*
|
};)*
|
||||||
$(let path = match <$variant_has_body_ty>::parse_path_with_arguments(path)? {
|
$(return Ok(Self::$Unknown(syn::TraitBound {
|
||||||
Ok(v) => return Ok(Ok(Self::$VariantHasBody(v))),
|
|
||||||
Err(path) => path,
|
|
||||||
};)*
|
|
||||||
$(return Ok(Ok(Self::$Unknown(syn::TraitBound {
|
|
||||||
paren_token: None,
|
paren_token: None,
|
||||||
modifier: syn::TraitBoundModifier::None,
|
modifier: syn::TraitBoundModifier::None,
|
||||||
lifetimes: None,
|
lifetimes: None,
|
||||||
path,
|
path,
|
||||||
}.into())));)?
|
}.into()));)?
|
||||||
Ok(Err(path))
|
Err(path)
|
||||||
}
|
}
|
||||||
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> syn::Result<Result<Self, syn::TypeParamBound>> {
|
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> {
|
||||||
#![allow(unreachable_code)]
|
#![allow(unreachable_code)]
|
||||||
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
|
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
|
||||||
if let syn::TraitBound {
|
if let syn::TraitBound {
|
||||||
|
|
@ -2355,24 +2121,24 @@ macro_rules! impl_bounds {
|
||||||
lifetimes: None,
|
lifetimes: None,
|
||||||
path: _,
|
path: _,
|
||||||
} = trait_bound {
|
} = trait_bound {
|
||||||
match Self::parse_path_with_arguments(trait_bound.path)? {
|
match Self::parse_path(trait_bound.path) {
|
||||||
Ok(retval) => return Ok(Ok(retval)),
|
Ok(retval) => return Ok(retval),
|
||||||
Err(path) => trait_bound.path = path,
|
Err(path) => trait_bound.path = path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type_param_bound = trait_bound.into();
|
type_param_bound = trait_bound.into();
|
||||||
}
|
}
|
||||||
$(return Ok(Ok(Self::$Unknown(type_param_bound)));)?
|
$(return Ok(Self::$Unknown(type_param_bound));)?
|
||||||
Ok(Err(type_param_bound))
|
Err(type_param_bound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for $enum_type {
|
impl Parse for $enum_type {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
Self::parse_type_param_bound(input.parse()?)?
|
Self::parse_type_param_bound(input.parse()?)
|
||||||
.map_err(|type_param_bound| syn::Error::new_spanned(
|
.map_err(|type_param_bound| syn::Error::new_spanned(
|
||||||
type_param_bound,
|
type_param_bound,
|
||||||
format_args!("expected one of: {}", [$(stringify!($Variant),)* $(stringify!($VariantHasBody)),*].join(", ")),
|
format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2381,7 +2147,6 @@ macro_rules! impl_bounds {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
$vis struct $struct_type {
|
$vis struct $struct_type {
|
||||||
$($vis $Variant: Option<known_items::$Variant>,)*
|
$($vis $Variant: Option<known_items::$Variant>,)*
|
||||||
$($vis $VariantHasBody: Option<$variant_has_body_ty>,)*
|
|
||||||
$($vis $Unknown: Vec<syn::TypeParamBound>,)?
|
$($vis $Unknown: Vec<syn::TypeParamBound>,)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2394,11 +2159,6 @@ macro_rules! impl_bounds {
|
||||||
separator = Some(<Token![+]>::default());
|
separator = Some(<Token![+]>::default());
|
||||||
v.to_tokens(tokens);
|
v.to_tokens(tokens);
|
||||||
})*
|
})*
|
||||||
$(if let Some(v) = &self.$VariantHasBody {
|
|
||||||
separator.to_tokens(tokens);
|
|
||||||
separator = Some(<Token![+]>::default());
|
|
||||||
v.to_tokens(tokens);
|
|
||||||
})*
|
|
||||||
$(for v in &self.$Unknown {
|
$(for v in &self.$Unknown {
|
||||||
separator.to_tokens(tokens);
|
separator.to_tokens(tokens);
|
||||||
separator = Some(<Token![+]>::default());
|
separator = Some(<Token![+]>::default());
|
||||||
|
|
@ -2412,7 +2172,6 @@ macro_rules! impl_bounds {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
$vis struct Iter {
|
$vis struct Iter {
|
||||||
$($Variant: Option<known_items::$Variant>,)*
|
$($Variant: Option<known_items::$Variant>,)*
|
||||||
$($VariantHasBody: Option<$variant_has_body_ty>,)*
|
|
||||||
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
|
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2423,7 +2182,6 @@ macro_rules! impl_bounds {
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Iter {
|
Iter {
|
||||||
$($Variant: self.$Variant,)*
|
$($Variant: self.$Variant,)*
|
||||||
$($VariantHasBody: self.$VariantHasBody,)*
|
|
||||||
$($Unknown: self.$Unknown.into_iter(),)?
|
$($Unknown: self.$Unknown.into_iter(),)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2438,11 +2196,6 @@ macro_rules! impl_bounds {
|
||||||
return Some($enum_type::$Variant(value));
|
return Some($enum_type::$Variant(value));
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
$(
|
|
||||||
if let Some(value) = self.$VariantHasBody.take() {
|
|
||||||
return Some($enum_type::$VariantHasBody(value));
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
$(
|
$(
|
||||||
if let Some(value) = self.$Unknown.next() {
|
if let Some(value) = self.$Unknown.next() {
|
||||||
return Some($enum_type::$Unknown(value));
|
return Some($enum_type::$Unknown(value));
|
||||||
|
|
@ -2458,11 +2211,6 @@ macro_rules! impl_bounds {
|
||||||
init = f(init, $enum_type::$Variant(value));
|
init = f(init, $enum_type::$Variant(value));
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
$(
|
|
||||||
if let Some(value) = self.$VariantHasBody.take() {
|
|
||||||
init = f(init, $enum_type::$VariantHasBody(value));
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
$(
|
$(
|
||||||
if let Some(value) = self.$Unknown.next() {
|
if let Some(value) = self.$Unknown.next() {
|
||||||
init = f(init, $enum_type::$Unknown(value));
|
init = f(init, $enum_type::$Unknown(value));
|
||||||
|
|
@ -2479,9 +2227,6 @@ macro_rules! impl_bounds {
|
||||||
$($enum_type::$Variant(v) => {
|
$($enum_type::$Variant(v) => {
|
||||||
self.$Variant = Some(v);
|
self.$Variant = Some(v);
|
||||||
})*
|
})*
|
||||||
$($enum_type::$VariantHasBody(v) => {
|
|
||||||
self.$VariantHasBody = Some(v);
|
|
||||||
})*
|
|
||||||
$($enum_type::$Unknown(v) => {
|
$($enum_type::$Unknown(v) => {
|
||||||
self.$Unknown.push(v);
|
self.$Unknown.push(v);
|
||||||
})?
|
})?
|
||||||
|
|
@ -2503,9 +2248,6 @@ macro_rules! impl_bounds {
|
||||||
$(if let Some(v) = v.$Variant {
|
$(if let Some(v) = v.$Variant {
|
||||||
self.$Variant = Some(v);
|
self.$Variant = Some(v);
|
||||||
})*
|
})*
|
||||||
$(if let Some(v) = v.$VariantHasBody {
|
|
||||||
self.$VariantHasBody = Some(v);
|
|
||||||
})*
|
|
||||||
$(self.$Unknown.extend(v.$Unknown);)*
|
$(self.$Unknown.extend(v.$Unknown);)*
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -2560,8 +2302,6 @@ impl_bounds! {
|
||||||
Size,
|
Size,
|
||||||
StaticType,
|
StaticType,
|
||||||
Type,
|
Type,
|
||||||
#[has_body]
|
|
||||||
PhantomConstGet(PhantomConstGetBound),
|
|
||||||
#[unknown]
|
#[unknown]
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
@ -2577,8 +2317,6 @@ impl_bounds! {
|
||||||
ResetType,
|
ResetType,
|
||||||
StaticType,
|
StaticType,
|
||||||
Type,
|
Type,
|
||||||
#[has_body]
|
|
||||||
PhantomConstGet(PhantomConstGetBound),
|
|
||||||
#[unknown]
|
#[unknown]
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
@ -2594,7 +2332,6 @@ impl From<ParsedTypeBound> for ParsedBound {
|
||||||
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
|
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
|
||||||
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
|
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
|
||||||
ParsedTypeBound::Type(v) => ParsedBound::Type(v),
|
ParsedTypeBound::Type(v) => ParsedBound::Type(v),
|
||||||
ParsedTypeBound::PhantomConstGet(v) => ParsedBound::PhantomConstGet(v),
|
|
||||||
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
|
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2610,7 +2347,6 @@ impl From<ParsedTypeBounds> for ParsedBounds {
|
||||||
ResetType,
|
ResetType,
|
||||||
StaticType,
|
StaticType,
|
||||||
Type,
|
Type,
|
||||||
PhantomConstGet,
|
|
||||||
Unknown,
|
Unknown,
|
||||||
} = value;
|
} = value;
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -2623,7 +2359,6 @@ impl From<ParsedTypeBounds> for ParsedBounds {
|
||||||
Size: None,
|
Size: None,
|
||||||
StaticType,
|
StaticType,
|
||||||
Type,
|
Type,
|
||||||
PhantomConstGet,
|
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2660,10 +2395,6 @@ impl ParsedTypeBound {
|
||||||
ParsedTypeBound::Type(known_items::Type(span)),
|
ParsedTypeBound::Type(known_items::Type(span)),
|
||||||
]),
|
]),
|
||||||
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
|
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
|
||||||
Self::PhantomConstGet(v) => ParsedTypeBounds::from_iter([
|
|
||||||
ParsedTypeBound::from(v),
|
|
||||||
ParsedTypeBound::Type(known_items::Type(span)),
|
|
||||||
]),
|
|
||||||
Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
|
Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2699,7 +2430,6 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
|
||||||
Size,
|
Size,
|
||||||
StaticType: None,
|
StaticType: None,
|
||||||
Type: None,
|
Type: None,
|
||||||
PhantomConstGet: None,
|
|
||||||
Unknown: vec![],
|
Unknown: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2802,9 +2532,6 @@ impl ParsedBound {
|
||||||
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
|
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
|
||||||
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
|
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
|
||||||
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
|
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
|
||||||
Self::PhantomConstGet(v) => {
|
|
||||||
ParsedBoundCategory::Type(ParsedTypeBound::PhantomConstGet(v))
|
|
||||||
}
|
|
||||||
Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
|
Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3023,7 +2750,7 @@ impl ParsedGenerics {
|
||||||
let span = ident.span();
|
let span = ident.span();
|
||||||
ItemStruct {
|
ItemStruct {
|
||||||
attrs: vec![
|
attrs: vec![
|
||||||
common_derives(span, true),
|
common_derives(span),
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
},
|
},
|
||||||
|
|
@ -3690,8 +3417,7 @@ impl ParsedGenerics {
|
||||||
| ParsedTypeBound::BundleType(_)
|
| ParsedTypeBound::BundleType(_)
|
||||||
| ParsedTypeBound::EnumType(_)
|
| ParsedTypeBound::EnumType(_)
|
||||||
| ParsedTypeBound::IntType(_)
|
| ParsedTypeBound::IntType(_)
|
||||||
| ParsedTypeBound::ResetType(_)
|
| ParsedTypeBound::ResetType(_) => {
|
||||||
| ParsedTypeBound::PhantomConstGet(_) => {
|
|
||||||
errors.error(bound, "bounds on mask types are not implemented");
|
errors.error(bound, "bounds on mask types are not implemented");
|
||||||
}
|
}
|
||||||
ParsedTypeBound::StaticType(bound) => {
|
ParsedTypeBound::StaticType(bound) => {
|
||||||
|
|
@ -4660,230 +4386,3 @@ impl MakeHdlTypeExpr for ParsedTypeTuple {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub(crate) enum ParsedSimpleVisibility {
|
|
||||||
Public(Token![pub]),
|
|
||||||
PubCrate {
|
|
||||||
pub_token: Token![pub],
|
|
||||||
paren_token: Paren,
|
|
||||||
crate_token: Token![crate],
|
|
||||||
},
|
|
||||||
Inherited,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ParsedSimpleVisibility> for Visibility {
|
|
||||||
fn from(value: ParsedSimpleVisibility) -> Self {
|
|
||||||
match value {
|
|
||||||
ParsedSimpleVisibility::Public(v) => Visibility::Public(v),
|
|
||||||
ParsedSimpleVisibility::PubCrate {
|
|
||||||
pub_token,
|
|
||||||
paren_token,
|
|
||||||
crate_token,
|
|
||||||
} => Visibility::Restricted(syn::VisRestricted {
|
|
||||||
pub_token,
|
|
||||||
paren_token,
|
|
||||||
in_token: None,
|
|
||||||
path: Box::new(Ident::new("crate", crate_token.span).into()),
|
|
||||||
}),
|
|
||||||
ParsedSimpleVisibility::Inherited => Visibility::Inherited,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for ParsedSimpleVisibility {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for ParsedSimpleVisibility {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.visibility_level().cmp(&other.visibility_level())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParsedSimpleVisibility {
|
|
||||||
const VISIBILITY_LEVEL_INHERITED: u8 = 0;
|
|
||||||
const VISIBILITY_LEVEL_RESTRICTED: u8 = 1 + Self::VISIBILITY_LEVEL_INHERITED;
|
|
||||||
const VISIBILITY_LEVEL_PUB_CRATE: u8 = 1 + Self::VISIBILITY_LEVEL_RESTRICTED;
|
|
||||||
const VISIBILITY_LEVEL_PUB: u8 = 1 + Self::VISIBILITY_LEVEL_PUB_CRATE;
|
|
||||||
fn visibility_level(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Self::Public(_) => Self::VISIBILITY_LEVEL_PUB,
|
|
||||||
Self::PubCrate { .. } => Self::VISIBILITY_LEVEL_PUB_CRATE,
|
|
||||||
Self::Inherited => Self::VISIBILITY_LEVEL_INHERITED,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn parse(vis: Visibility) -> Result<Self, syn::VisRestricted> {
|
|
||||||
match vis {
|
|
||||||
Visibility::Public(v) => Ok(Self::Public(v)),
|
|
||||||
Visibility::Restricted(syn::VisRestricted {
|
|
||||||
pub_token,
|
|
||||||
paren_token,
|
|
||||||
in_token: None,
|
|
||||||
path,
|
|
||||||
}) if path.is_ident("crate") => Ok(Self::PubCrate {
|
|
||||||
pub_token,
|
|
||||||
paren_token,
|
|
||||||
crate_token: Token.expect("just checked").span()),
|
|
||||||
}),
|
|
||||||
Visibility::Restricted(v) => Err(v),
|
|
||||||
Visibility::Inherited => Ok(Self::Inherited),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub(crate) enum ParsedVisibility {
|
|
||||||
Simple(ParsedSimpleVisibility),
|
|
||||||
Restricted(syn::VisRestricted),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ParsedVisibility> for Visibility {
|
|
||||||
fn from(value: ParsedVisibility) -> Self {
|
|
||||||
match value {
|
|
||||||
ParsedVisibility::Simple(v) => v.into(),
|
|
||||||
ParsedVisibility::Restricted(v) => Visibility::Restricted(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for ParsedVisibility {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
match (self, other) {
|
|
||||||
(ParsedVisibility::Simple(l), ParsedVisibility::Simple(r)) => Some(l.cmp(r)),
|
|
||||||
(ParsedVisibility::Simple(l), ParsedVisibility::Restricted(_)) => Some(
|
|
||||||
l.visibility_level()
|
|
||||||
.cmp(&ParsedSimpleVisibility::VISIBILITY_LEVEL_RESTRICTED),
|
|
||||||
),
|
|
||||||
(ParsedVisibility::Restricted(_), ParsedVisibility::Simple(r)) => {
|
|
||||||
Some(ParsedSimpleVisibility::VISIBILITY_LEVEL_RESTRICTED.cmp(&r.visibility_level()))
|
|
||||||
}
|
|
||||||
(ParsedVisibility::Restricted(l), ParsedVisibility::Restricted(r)) => {
|
|
||||||
(l == r).then_some(std::cmp::Ordering::Equal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParsedVisibility {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn parse(vis: Visibility) -> Self {
|
|
||||||
match ParsedSimpleVisibility::parse(vis) {
|
|
||||||
Ok(simple) => Self::Simple(simple),
|
|
||||||
Err(restricted) => Self::Restricted(restricted),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn min<'a>(&'a self, other: &'a Self) -> Option<&'a Self> {
|
|
||||||
self.partial_cmp(other)
|
|
||||||
.map(|ord| if ord.is_lt() { self } else { other })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct CustomDebugTrait<'a> {
|
|
||||||
pub(crate) trait_path: &'a Path,
|
|
||||||
pub(crate) fn_name: &'a Ident,
|
|
||||||
pub(crate) this_arg: &'a FnArg,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn create_struct_debug_impl(
|
|
||||||
item_struct: &ItemStruct,
|
|
||||||
debug_struct_name: &str,
|
|
||||||
custom_debug_trait: Option<CustomDebugTrait<'_>>,
|
|
||||||
) -> TokenStream {
|
|
||||||
let ident = &item_struct.ident;
|
|
||||||
let span = ident.span();
|
|
||||||
let (impl_generics, type_generics, where_clause) = item_struct.generics.split_for_impl();
|
|
||||||
let trait_path;
|
|
||||||
let fn_name;
|
|
||||||
let this_arg;
|
|
||||||
let CustomDebugTrait {
|
|
||||||
trait_path,
|
|
||||||
fn_name,
|
|
||||||
this_arg,
|
|
||||||
} = match custom_debug_trait {
|
|
||||||
Some(v) => v,
|
|
||||||
None => {
|
|
||||||
trait_path = parse_quote_spanned! {span=>
|
|
||||||
::fayalite::__std::fmt::Debug
|
|
||||||
};
|
|
||||||
fn_name = parse_quote_spanned! {span=>
|
|
||||||
fmt
|
|
||||||
};
|
|
||||||
this_arg = parse_quote_spanned! {span=>
|
|
||||||
&self
|
|
||||||
};
|
|
||||||
CustomDebugTrait {
|
|
||||||
trait_path: &trait_path,
|
|
||||||
fn_name: &fn_name,
|
|
||||||
this_arg: &this_arg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let this_arg_name = match this_arg {
|
|
||||||
FnArg::Receiver(this_arg) => this_arg.self_token.to_token_stream(),
|
|
||||||
FnArg::Typed(this_arg) => match &*this_arg.pat {
|
|
||||||
syn::Pat::Ident(pat_ident) => pat_ident.ident.to_token_stream(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
match &item_struct.fields {
|
|
||||||
Fields::Named(fields) => {
|
|
||||||
let field_idents = fields
|
|
||||||
.named
|
|
||||||
.iter()
|
|
||||||
.map(|v| v.ident.as_ref().expect("known to have field name"));
|
|
||||||
let field_names = field_idents.clone().map(|v| v.to_string());
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics #trait_path for #ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
|
|
||||||
let _ = #this_arg_name;
|
|
||||||
f.debug_struct(#debug_struct_name)
|
|
||||||
#(.field(#field_names, &#this_arg_name.#field_idents))*
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Fields::Unnamed(fields) => {
|
|
||||||
let field_members = fields
|
|
||||||
.unnamed
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, _)| syn::Index {
|
|
||||||
index: index as _,
|
|
||||||
span,
|
|
||||||
});
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics #trait_path for #ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
|
|
||||||
let _ = #this_arg_name;
|
|
||||||
f.debug_tuple(#debug_struct_name)
|
|
||||||
#(.field(&#this_arg_name.#field_members))*
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Fields::Unit => quote_spanned! {ident.span()=>
|
|
||||||
#[automatically_derived]
|
|
||||||
impl #impl_generics #trait_path for #ident #type_generics
|
|
||||||
#where_clause
|
|
||||||
{
|
|
||||||
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
|
|
||||||
let _ = #this_arg_name;
|
|
||||||
f.write_str(#debug_struct_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ pub(crate) trait CustomToken:
|
||||||
|
|
||||||
mod kw {
|
mod kw {
|
||||||
pub(crate) use syn::token::Extern as extern_;
|
pub(crate) use syn::token::Extern as extern_;
|
||||||
pub(crate) use syn::token::Type as type_;
|
|
||||||
|
|
||||||
macro_rules! custom_keyword {
|
macro_rules! custom_keyword {
|
||||||
($kw:ident) => {
|
($kw:ident) => {
|
||||||
|
|
@ -67,7 +66,6 @@ mod kw {
|
||||||
}
|
}
|
||||||
|
|
||||||
custom_keyword!(__evaluated_cfgs);
|
custom_keyword!(__evaluated_cfgs);
|
||||||
custom_keyword!(add_platform_io);
|
|
||||||
custom_keyword!(all);
|
custom_keyword!(all);
|
||||||
custom_keyword!(any);
|
custom_keyword!(any);
|
||||||
custom_keyword!(cfg);
|
custom_keyword!(cfg);
|
||||||
|
|
@ -76,18 +74,13 @@ mod kw {
|
||||||
custom_keyword!(cmp_eq);
|
custom_keyword!(cmp_eq);
|
||||||
custom_keyword!(connect_inexact);
|
custom_keyword!(connect_inexact);
|
||||||
custom_keyword!(custom_bounds);
|
custom_keyword!(custom_bounds);
|
||||||
custom_keyword!(custom_debug);
|
|
||||||
custom_keyword!(custom_sim_display);
|
|
||||||
custom_keyword!(flip);
|
custom_keyword!(flip);
|
||||||
custom_keyword!(get);
|
|
||||||
custom_keyword!(hdl);
|
custom_keyword!(hdl);
|
||||||
custom_keyword!(hdl_module);
|
custom_keyword!(hdl_module);
|
||||||
custom_keyword!(incomplete_wire);
|
custom_keyword!(incomplete_wire);
|
||||||
custom_keyword!(input);
|
custom_keyword!(input);
|
||||||
custom_keyword!(instance);
|
custom_keyword!(instance);
|
||||||
custom_keyword!(m);
|
custom_keyword!(m);
|
||||||
custom_keyword!(mask_sim);
|
|
||||||
custom_keyword!(mask_type);
|
|
||||||
custom_keyword!(memory);
|
custom_keyword!(memory);
|
||||||
custom_keyword!(memory_array);
|
custom_keyword!(memory_array);
|
||||||
custom_keyword!(memory_with_init);
|
custom_keyword!(memory_with_init);
|
||||||
|
|
@ -892,13 +885,7 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _print_on_panic = PrintOnPanic(&contents);
|
let _print_on_panic = PrintOnPanic(&contents);
|
||||||
let mut parse_err = None;
|
let contents = prettyplease::unparse(&parse_quote! { #contents });
|
||||||
let (Ok(contents) | Err(contents)) = syn::parse2(contents.clone())
|
|
||||||
.map(|file| prettyplease::unparse(&file))
|
|
||||||
.map_err(|e| {
|
|
||||||
parse_err = Some(e);
|
|
||||||
contents.to_string()
|
|
||||||
});
|
|
||||||
let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
|
let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
|
||||||
let hash = base16ct::HexDisplay(&hash[..5]);
|
let hash = base16ct::HexDisplay(&hash[..5]);
|
||||||
file.write_all(contents.as_bytes()).unwrap();
|
file.write_all(contents.as_bytes()).unwrap();
|
||||||
|
|
@ -910,26 +897,9 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
|
||||||
e.unwrap();
|
e.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let log_msg = if let Some(parse_err) = parse_err {
|
eprintln!("generated {}", dest_file.display());
|
||||||
format!(
|
|
||||||
"fayalite-proc-macros-impl internal error:\nfailed to parse generated output: {parse_err}\nunformatted output is in: {}\n",
|
|
||||||
dest_file.display()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!("generated {}\n", dest_file.display())
|
|
||||||
};
|
|
||||||
// write message atomically if possible
|
|
||||||
let mut stderr = std::io::stderr().lock();
|
|
||||||
let write_result = stderr.write_all(log_msg.as_bytes());
|
|
||||||
let flush_result = stderr.flush();
|
|
||||||
drop(stderr); // unlock before we try to panic
|
|
||||||
write_result.unwrap();
|
|
||||||
flush_result.unwrap();
|
|
||||||
std::io::stderr()
|
|
||||||
.lock()
|
|
||||||
.write_all(log_msg.as_bytes())
|
|
||||||
.unwrap();
|
|
||||||
let dest_file = dest_file.to_str().unwrap();
|
let dest_file = dest_file.to_str().unwrap();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
include!(#dest_file);
|
include!(#dest_file);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
Errors, HdlAttr, PairsIterExt,
|
Errors, HdlAttr, PairsIterExt,
|
||||||
hdl_type_common::{ParsedGenerics, SplitForImpl},
|
hdl_type_common::{ParsedGenerics, SplitForImpl},
|
||||||
kw,
|
kw,
|
||||||
module::transform_body::{HdlLet, HdlLetKindIO, ModuleIOOrAddPlatformIO},
|
module::transform_body::{HdlLet, HdlLetKindIO},
|
||||||
options,
|
options,
|
||||||
};
|
};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
|
@ -39,7 +39,7 @@ pub(crate) fn check_name_conflicts_with_module_builder(name: &Ident) -> syn::Res
|
||||||
if name == "m" {
|
if name == "m" {
|
||||||
Err(Error::new_spanned(
|
Err(Error::new_spanned(
|
||||||
name,
|
name,
|
||||||
"name conflicts with implicit `m: &ModuleBuilder`",
|
"name conflicts with implicit `m: &mut ModuleBuilder<_>`",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -67,7 +67,7 @@ struct ModuleFnModule {
|
||||||
vis: Visibility,
|
vis: Visibility,
|
||||||
sig: Signature,
|
sig: Signature,
|
||||||
block: Box<Block>,
|
block: Box<Block>,
|
||||||
struct_generics: Option<ParsedGenerics>,
|
struct_generics: ParsedGenerics,
|
||||||
the_struct: TokenStream,
|
the_struct: TokenStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,7 +290,7 @@ impl ModuleFn {
|
||||||
paren_token,
|
paren_token,
|
||||||
body,
|
body,
|
||||||
} => {
|
} => {
|
||||||
debug_assert!(matches!(io, ModuleIOOrAddPlatformIO::ModuleIO(v) if v.is_empty()));
|
debug_assert!(io.is_empty());
|
||||||
return Ok(Self(ModuleFnImpl::Fn {
|
return Ok(Self(ModuleFnImpl::Fn {
|
||||||
attrs,
|
attrs,
|
||||||
config_options: HdlAttr {
|
config_options: HdlAttr {
|
||||||
|
|
@ -322,21 +322,6 @@ impl ModuleFn {
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let io = match io {
|
|
||||||
ModuleIOOrAddPlatformIO::ModuleIO(io) => io,
|
|
||||||
ModuleIOOrAddPlatformIO::AddPlatformIO => {
|
|
||||||
return Ok(Self(ModuleFnImpl::Module(ModuleFnModule {
|
|
||||||
attrs,
|
|
||||||
config_options,
|
|
||||||
module_kind: module_kind.unwrap(),
|
|
||||||
vis,
|
|
||||||
sig,
|
|
||||||
block,
|
|
||||||
struct_generics: None,
|
|
||||||
the_struct: TokenStream::new(),
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let (_struct_impl_generics, _struct_type_generics, struct_where_clause) =
|
let (_struct_impl_generics, _struct_type_generics, struct_where_clause) =
|
||||||
struct_generics.split_for_impl();
|
struct_generics.split_for_impl();
|
||||||
let struct_where_clause: Option<WhereClause> = parse_quote! { #struct_where_clause };
|
let struct_where_clause: Option<WhereClause> = parse_quote! { #struct_where_clause };
|
||||||
|
|
@ -379,7 +364,7 @@ impl ModuleFn {
|
||||||
vis,
|
vis,
|
||||||
sig,
|
sig,
|
||||||
block,
|
block,
|
||||||
struct_generics: Some(struct_generics),
|
struct_generics,
|
||||||
the_struct,
|
the_struct,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
@ -448,14 +433,9 @@ impl ModuleFn {
|
||||||
ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal },
|
ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal },
|
||||||
};
|
};
|
||||||
let fn_name = &outer_sig.ident;
|
let fn_name = &outer_sig.ident;
|
||||||
let struct_ty = match struct_generics {
|
|
||||||
Some(struct_generics) => {
|
|
||||||
let (_struct_impl_generics, struct_type_generics, _struct_where_clause) =
|
let (_struct_impl_generics, struct_type_generics, _struct_where_clause) =
|
||||||
struct_generics.split_for_impl();
|
struct_generics.split_for_impl();
|
||||||
quote! {#fn_name #struct_type_generics}
|
let struct_ty = quote! {#fn_name #struct_type_generics};
|
||||||
}
|
|
||||||
None => quote! {::fayalite::bundle::Bundle},
|
|
||||||
};
|
|
||||||
body_sig.ident = parse_quote! {__body};
|
body_sig.ident = parse_quote! {__body};
|
||||||
body_sig
|
body_sig
|
||||||
.inputs
|
.inputs
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ options! {
|
||||||
pub(crate) enum LetFnKind {
|
pub(crate) enum LetFnKind {
|
||||||
Input(input),
|
Input(input),
|
||||||
Output(output),
|
Output(output),
|
||||||
AddPlatformIO(add_platform_io),
|
|
||||||
Instance(instance),
|
Instance(instance),
|
||||||
RegBuilder(reg_builder),
|
RegBuilder(reg_builder),
|
||||||
Wire(wire),
|
Wire(wire),
|
||||||
|
|
@ -217,49 +216,6 @@ impl HdlLetKindToTokens for HdlLetKindInstance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub(crate) struct HdlLetKindAddPlatformIO {
|
|
||||||
pub(crate) m: kw::m,
|
|
||||||
pub(crate) dot_token: Token![.],
|
|
||||||
pub(crate) add_platform_io: kw::add_platform_io,
|
|
||||||
pub(crate) paren: Paren,
|
|
||||||
pub(crate) platform_io_builder: Box<Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParseTypes<Self> for HdlLetKindAddPlatformIO {
|
|
||||||
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
|
||||||
Ok(input.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_fold! {
|
|
||||||
struct HdlLetKindAddPlatformIO<> {
|
|
||||||
m: kw::m,
|
|
||||||
dot_token: Token![.],
|
|
||||||
add_platform_io: kw::add_platform_io,
|
|
||||||
paren: Paren,
|
|
||||||
platform_io_builder: Box<Expr>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HdlLetKindToTokens for HdlLetKindAddPlatformIO {
|
|
||||||
fn ty_to_tokens(&self, _tokens: &mut TokenStream) {}
|
|
||||||
|
|
||||||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let Self {
|
|
||||||
m,
|
|
||||||
dot_token,
|
|
||||||
add_platform_io,
|
|
||||||
paren,
|
|
||||||
platform_io_builder,
|
|
||||||
} = self;
|
|
||||||
m.to_tokens(tokens);
|
|
||||||
dot_token.to_tokens(tokens);
|
|
||||||
add_platform_io.to_tokens(tokens);
|
|
||||||
paren.surround(tokens, |tokens| platform_io_builder.to_tokens(tokens));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct RegBuilderClockDomain {
|
pub(crate) struct RegBuilderClockDomain {
|
||||||
pub(crate) dot_token: Token![.],
|
pub(crate) dot_token: Token![.],
|
||||||
|
|
@ -755,7 +711,6 @@ impl HdlLetKindMemory {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) enum HdlLetKind<IOType = ParsedType> {
|
pub(crate) enum HdlLetKind<IOType = ParsedType> {
|
||||||
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
||||||
AddPlatformIO(HdlLetKindAddPlatformIO),
|
|
||||||
Incomplete(HdlLetKindIncomplete),
|
Incomplete(HdlLetKindIncomplete),
|
||||||
Instance(HdlLetKindInstance),
|
Instance(HdlLetKindInstance),
|
||||||
RegBuilder(HdlLetKindRegBuilder),
|
RegBuilder(HdlLetKindRegBuilder),
|
||||||
|
|
@ -766,7 +721,6 @@ pub(crate) enum HdlLetKind<IOType = ParsedType> {
|
||||||
impl_fold! {
|
impl_fold! {
|
||||||
enum HdlLetKind<IOType,> {
|
enum HdlLetKind<IOType,> {
|
||||||
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
||||||
AddPlatformIO(HdlLetKindAddPlatformIO),
|
|
||||||
Incomplete(HdlLetKindIncomplete),
|
Incomplete(HdlLetKindIncomplete),
|
||||||
Instance(HdlLetKindInstance),
|
Instance(HdlLetKindInstance),
|
||||||
RegBuilder(HdlLetKindRegBuilder),
|
RegBuilder(HdlLetKindRegBuilder),
|
||||||
|
|
@ -782,9 +736,6 @@ impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> {
|
||||||
) -> Result<Self, ParseFailed> {
|
) -> Result<Self, ParseFailed> {
|
||||||
match input {
|
match input {
|
||||||
HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO),
|
HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO),
|
||||||
HdlLetKind::AddPlatformIO(input) => {
|
|
||||||
ParseTypes::parse_types(input, parser).map(HdlLetKind::AddPlatformIO)
|
|
||||||
}
|
|
||||||
HdlLetKind::Incomplete(input) => {
|
HdlLetKind::Incomplete(input) => {
|
||||||
ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete)
|
ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete)
|
||||||
}
|
}
|
||||||
|
|
@ -910,23 +861,6 @@ impl HdlLetKindParse for HdlLetKind<Type> {
|
||||||
ModuleIOKind::Output(output),
|
ModuleIOKind::Output(output),
|
||||||
)
|
)
|
||||||
.map(Self::IO),
|
.map(Self::IO),
|
||||||
LetFnKind::AddPlatformIO((add_platform_io,)) => {
|
|
||||||
if let Some(parsed_ty) = parsed_ty {
|
|
||||||
return Err(Error::new_spanned(
|
|
||||||
parsed_ty.1,
|
|
||||||
"type annotation not allowed for instance",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let (m, dot_token) = unwrap_m_dot(m_dot, kind)?;
|
|
||||||
let paren_contents;
|
|
||||||
Ok(Self::AddPlatformIO(HdlLetKindAddPlatformIO {
|
|
||||||
m,
|
|
||||||
dot_token,
|
|
||||||
add_platform_io,
|
|
||||||
paren: parenthesized!(paren_contents in input),
|
|
||||||
platform_io_builder: paren_contents.call(parse_single_fn_arg)?,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
LetFnKind::Instance((instance,)) => {
|
LetFnKind::Instance((instance,)) => {
|
||||||
if let Some(parsed_ty) = parsed_ty {
|
if let Some(parsed_ty) = parsed_ty {
|
||||||
return Err(Error::new_spanned(
|
return Err(Error::new_spanned(
|
||||||
|
|
@ -1002,7 +936,6 @@ impl HdlLetKindToTokens for HdlLetKind {
|
||||||
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
|
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
match self {
|
match self {
|
||||||
HdlLetKind::IO(v) => v.ty_to_tokens(tokens),
|
HdlLetKind::IO(v) => v.ty_to_tokens(tokens),
|
||||||
HdlLetKind::AddPlatformIO(v) => v.ty_to_tokens(tokens),
|
|
||||||
HdlLetKind::Incomplete(v) => v.ty_to_tokens(tokens),
|
HdlLetKind::Incomplete(v) => v.ty_to_tokens(tokens),
|
||||||
HdlLetKind::Instance(v) => v.ty_to_tokens(tokens),
|
HdlLetKind::Instance(v) => v.ty_to_tokens(tokens),
|
||||||
HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens),
|
HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens),
|
||||||
|
|
@ -1014,7 +947,6 @@ impl HdlLetKindToTokens for HdlLetKind {
|
||||||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
match self {
|
match self {
|
||||||
HdlLetKind::IO(v) => v.expr_to_tokens(tokens),
|
HdlLetKind::IO(v) => v.expr_to_tokens(tokens),
|
||||||
HdlLetKind::AddPlatformIO(v) => v.expr_to_tokens(tokens),
|
|
||||||
HdlLetKind::Incomplete(v) => v.expr_to_tokens(tokens),
|
HdlLetKind::Incomplete(v) => v.expr_to_tokens(tokens),
|
||||||
HdlLetKind::Instance(v) => v.expr_to_tokens(tokens),
|
HdlLetKind::Instance(v) => v.expr_to_tokens(tokens),
|
||||||
HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens),
|
HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens),
|
||||||
|
|
@ -1217,7 +1149,7 @@ impl<T: ToString> ToTokens for ImplicitName<T> {
|
||||||
struct Visitor<'a> {
|
struct Visitor<'a> {
|
||||||
module_kind: Option<ModuleKind>,
|
module_kind: Option<ModuleKind>,
|
||||||
errors: Errors,
|
errors: Errors,
|
||||||
io: ModuleIOOrAddPlatformIO,
|
io: Vec<ModuleIO>,
|
||||||
block_depth: usize,
|
block_depth: usize,
|
||||||
parsed_generics: &'a ParsedGenerics,
|
parsed_generics: &'a ParsedGenerics,
|
||||||
}
|
}
|
||||||
|
|
@ -1357,81 +1289,7 @@ impl Visitor<'_> {
|
||||||
}),
|
}),
|
||||||
semi_token: hdl_let.semi_token,
|
semi_token: hdl_let.semi_token,
|
||||||
};
|
};
|
||||||
match &mut self.io {
|
self.io.push(hdl_let);
|
||||||
ModuleIOOrAddPlatformIO::ModuleIO(io) => io.push(hdl_let),
|
|
||||||
ModuleIOOrAddPlatformIO::AddPlatformIO => {
|
|
||||||
self.errors.error(
|
|
||||||
kind,
|
|
||||||
"can't have other inputs/outputs in a module using m.add_platform_io()",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let_stmt
|
|
||||||
}
|
|
||||||
fn process_hdl_let_add_platform_io(
|
|
||||||
&mut self,
|
|
||||||
hdl_let: HdlLet<HdlLetKindAddPlatformIO>,
|
|
||||||
) -> Local {
|
|
||||||
let HdlLet {
|
|
||||||
mut attrs,
|
|
||||||
hdl_attr: _,
|
|
||||||
let_token,
|
|
||||||
mut_token,
|
|
||||||
ref name,
|
|
||||||
eq_token,
|
|
||||||
kind:
|
|
||||||
HdlLetKindAddPlatformIO {
|
|
||||||
m,
|
|
||||||
dot_token,
|
|
||||||
add_platform_io,
|
|
||||||
paren,
|
|
||||||
platform_io_builder,
|
|
||||||
},
|
|
||||||
semi_token,
|
|
||||||
} = hdl_let;
|
|
||||||
let mut expr = quote! {#m #dot_token #add_platform_io};
|
|
||||||
paren.surround(&mut expr, |expr| {
|
|
||||||
let name_str = ImplicitName {
|
|
||||||
name,
|
|
||||||
span: name.span(),
|
|
||||||
};
|
|
||||||
quote_spanned! {name.span()=>
|
|
||||||
#name_str, #platform_io_builder
|
|
||||||
}
|
|
||||||
.to_tokens(expr);
|
|
||||||
});
|
|
||||||
self.require_module(add_platform_io);
|
|
||||||
attrs.push(parse_quote_spanned! {let_token.span=>
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
});
|
|
||||||
let let_stmt = Local {
|
|
||||||
attrs,
|
|
||||||
let_token,
|
|
||||||
pat: parse_quote! { #mut_token #name },
|
|
||||||
init: Some(LocalInit {
|
|
||||||
eq_token,
|
|
||||||
expr: parse_quote! { #expr },
|
|
||||||
diverge: None,
|
|
||||||
}),
|
|
||||||
semi_token,
|
|
||||||
};
|
|
||||||
match &mut self.io {
|
|
||||||
ModuleIOOrAddPlatformIO::ModuleIO(io) => {
|
|
||||||
for io in io {
|
|
||||||
self.errors.error(
|
|
||||||
io.kind.kind,
|
|
||||||
"can't have other inputs/outputs in a module using m.add_platform_io()",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ModuleIOOrAddPlatformIO::AddPlatformIO => {
|
|
||||||
self.errors.error(
|
|
||||||
add_platform_io,
|
|
||||||
"can't use m.add_platform_io() more than once in a single module",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.io = ModuleIOOrAddPlatformIO::AddPlatformIO;
|
|
||||||
let_stmt
|
let_stmt
|
||||||
}
|
}
|
||||||
fn process_hdl_let_instance(&mut self, hdl_let: HdlLet<HdlLetKindInstance>) -> Local {
|
fn process_hdl_let_instance(&mut self, hdl_let: HdlLet<HdlLetKindInstance>) -> Local {
|
||||||
|
|
@ -1652,7 +1510,6 @@ impl Visitor<'_> {
|
||||||
}
|
}
|
||||||
the_match! {
|
the_match! {
|
||||||
IO => process_hdl_let_io,
|
IO => process_hdl_let_io,
|
||||||
AddPlatformIO => process_hdl_let_add_platform_io,
|
|
||||||
Incomplete => process_hdl_let_incomplete,
|
Incomplete => process_hdl_let_incomplete,
|
||||||
Instance => process_hdl_let_instance,
|
Instance => process_hdl_let_instance,
|
||||||
RegBuilder => process_hdl_let_reg_builder,
|
RegBuilder => process_hdl_let_reg_builder,
|
||||||
|
|
@ -1896,20 +1753,15 @@ impl Fold for Visitor<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum ModuleIOOrAddPlatformIO {
|
|
||||||
ModuleIO(Vec<ModuleIO>),
|
|
||||||
AddPlatformIO,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn transform_body(
|
pub(crate) fn transform_body(
|
||||||
module_kind: Option<ModuleKind>,
|
module_kind: Option<ModuleKind>,
|
||||||
mut body: Box<Block>,
|
mut body: Box<Block>,
|
||||||
parsed_generics: &ParsedGenerics,
|
parsed_generics: &ParsedGenerics,
|
||||||
) -> syn::Result<(Box<Block>, ModuleIOOrAddPlatformIO)> {
|
) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {
|
||||||
let mut visitor = Visitor {
|
let mut visitor = Visitor {
|
||||||
module_kind,
|
module_kind,
|
||||||
errors: Errors::new(),
|
errors: Errors::new(),
|
||||||
io: ModuleIOOrAddPlatformIO::ModuleIO(vec![]),
|
io: vec![],
|
||||||
block_depth: 0,
|
block_depth: 0,
|
||||||
parsed_generics,
|
parsed_generics,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,6 @@ impl Visitor<'_> {
|
||||||
field.expr = parse_quote_spanned! {field.member.span()=>
|
field.expr = parse_quote_spanned! {field.member.span()=>
|
||||||
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
|
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
|
||||||
};
|
};
|
||||||
field
|
|
||||||
.colon_token
|
|
||||||
.get_or_insert(Token));
|
|
||||||
}
|
}
|
||||||
return parse_quote_spanned! {name_span=>
|
return parse_quote_spanned! {name_span=>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned};
|
use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeSet;
|
||||||
use syn::{
|
use syn::{
|
||||||
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
|
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
|
||||||
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
|
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
|
||||||
|
|
@ -24,65 +24,65 @@ use syn::{
|
||||||
|
|
||||||
macro_rules! visit_trait {
|
macro_rules! visit_trait {
|
||||||
(
|
(
|
||||||
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &mut $Value:ty) $block:block)*
|
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)*
|
||||||
) => {
|
) => {
|
||||||
trait VisitMatchPat<'a> {
|
trait VisitMatchPat<'a> {
|
||||||
$(fn $fn(&mut self, $value: &'a mut $Value) {
|
$(fn $fn(&mut self, $value: &'a $Value) {
|
||||||
$fn(self, $value);
|
$fn(self, $value);
|
||||||
})*
|
})*
|
||||||
}
|
}
|
||||||
|
|
||||||
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a mut $Value) $block)*
|
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_trait! {
|
visit_trait! {
|
||||||
fn visit_match_pat_binding(_state: _, v: &mut MatchPatBinding) {
|
fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) {
|
||||||
let MatchPatBinding { mutability: _, ident: _ } = v;
|
let MatchPatBinding { ident: _ } = v;
|
||||||
}
|
}
|
||||||
fn visit_match_pat_wild(_state: _, v: &mut MatchPatWild) {
|
fn visit_match_pat_wild(_state: _, v: &MatchPatWild) {
|
||||||
let MatchPatWild { underscore_token: _ } = v;
|
let MatchPatWild { underscore_token: _ } = v;
|
||||||
}
|
}
|
||||||
fn visit_match_pat_rest(_state: _, v: &mut MatchPatRest) {
|
fn visit_match_pat_rest(_state: _, v: &MatchPatRest) {
|
||||||
let MatchPatRest { dot2_token: _ } = v;
|
let MatchPatRest { dot2_token: _ } = v;
|
||||||
}
|
}
|
||||||
fn visit_match_pat_paren(state: _, v: &mut MatchPatParen<MatchPat>) {
|
fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) {
|
||||||
let MatchPatParen { paren_token: _, pat } = v;
|
let MatchPatParen { paren_token: _, pat } = v;
|
||||||
state.visit_match_pat(pat);
|
state.visit_match_pat(pat);
|
||||||
}
|
}
|
||||||
fn visit_match_pat_paren_simple(state: _, v: &mut MatchPatParen<MatchPatSimple>) {
|
fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) {
|
||||||
let MatchPatParen { paren_token: _, pat } = v;
|
let MatchPatParen { paren_token: _, pat } = v;
|
||||||
state.visit_match_pat_simple(pat);
|
state.visit_match_pat_simple(pat);
|
||||||
}
|
}
|
||||||
fn visit_match_pat_or(state: _, v: &mut MatchPatOr<MatchPat>) {
|
fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) {
|
||||||
let MatchPatOr { leading_vert: _, cases } = v;
|
let MatchPatOr { leading_vert: _, cases } = v;
|
||||||
for v in cases {
|
for v in cases {
|
||||||
state.visit_match_pat(v);
|
state.visit_match_pat(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn visit_match_pat_or_simple(state: _, v: &mut MatchPatOr<MatchPatSimple>) {
|
fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) {
|
||||||
let MatchPatOr { leading_vert: _, cases } = v;
|
let MatchPatOr { leading_vert: _, cases } = v;
|
||||||
for v in cases {
|
for v in cases {
|
||||||
state.visit_match_pat_simple(v);
|
state.visit_match_pat_simple(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn visit_match_pat_struct_field(state: _, v: &mut MatchPatStructField) {
|
fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) {
|
||||||
let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
|
let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
|
||||||
state.visit_match_pat_simple(pat);
|
state.visit_match_pat_simple(pat);
|
||||||
}
|
}
|
||||||
fn visit_match_pat_struct(state: _, v: &mut MatchPatStruct) {
|
fn visit_match_pat_struct(state: _, v: &MatchPatStruct) {
|
||||||
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
|
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
|
||||||
for v in fields {
|
for v in fields {
|
||||||
state.visit_match_pat_struct_field(v);
|
state.visit_match_pat_struct_field(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn visit_match_pat_tuple(state: _, v: &mut MatchPatTuple) {
|
fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) {
|
||||||
let MatchPatTuple { paren_token: _, fields } = v;
|
let MatchPatTuple { paren_token: _, fields } = v;
|
||||||
for v in fields {
|
for v in fields {
|
||||||
state.visit_match_pat_simple(v);
|
state.visit_match_pat_simple(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn visit_match_pat_enum_variant(state: _, v: &mut MatchPatEnumVariant) {
|
fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) {
|
||||||
let MatchPatEnumVariant {
|
let MatchPatEnumVariant {
|
||||||
match_span:_,
|
match_span:_,
|
||||||
sim:_,
|
sim:_,
|
||||||
|
|
@ -95,7 +95,7 @@ visit_trait! {
|
||||||
state.visit_match_pat_simple(v);
|
state.visit_match_pat_simple(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn visit_match_pat_simple(state: _, v: &mut MatchPatSimple) {
|
fn visit_match_pat_simple(state: _, v: &MatchPatSimple) {
|
||||||
match v {
|
match v {
|
||||||
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
|
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
|
||||||
MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v),
|
MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v),
|
||||||
|
|
@ -104,7 +104,7 @@ visit_trait! {
|
||||||
MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v),
|
MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn visit_match_pat(state: _, v: &mut MatchPat) {
|
fn visit_match_pat(state: _, v: &MatchPat) {
|
||||||
match v {
|
match v {
|
||||||
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
|
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
|
||||||
MatchPat::Or(v) => state.visit_match_pat_or(v),
|
MatchPat::Or(v) => state.visit_match_pat_or(v),
|
||||||
|
|
@ -118,15 +118,13 @@ visit_trait! {
|
||||||
|
|
||||||
with_debug_clone_and_fold! {
|
with_debug_clone_and_fold! {
|
||||||
struct MatchPatBinding<> {
|
struct MatchPatBinding<> {
|
||||||
mutability: Option<Token![mut]>,
|
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for MatchPatBinding {
|
impl ToTokens for MatchPatBinding {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
let Self { mutability, ident } = self;
|
let Self { ident } = self;
|
||||||
mutability.to_tokens(tokens);
|
|
||||||
ident.to_tokens(tokens);
|
ident.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -213,20 +211,12 @@ impl ToTokens for MatchPatStructField {
|
||||||
colon_token,
|
colon_token,
|
||||||
pat,
|
pat,
|
||||||
} = self;
|
} = self;
|
||||||
if let (
|
field_name.to_tokens(tokens);
|
||||||
None,
|
if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
|
||||||
MatchPatSimple::Binding(MatchPatBinding {
|
|
||||||
mutability: _,
|
|
||||||
ident,
|
|
||||||
}),
|
|
||||||
) = (colon_token, pat)
|
|
||||||
{
|
|
||||||
if field_name == ident {
|
if field_name == ident {
|
||||||
pat.to_tokens(tokens);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field_name.to_tokens(tokens);
|
|
||||||
colon_token
|
colon_token
|
||||||
.unwrap_or_else(|| Token))
|
.unwrap_or_else(|| Token))
|
||||||
.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
|
|
@ -460,7 +450,7 @@ trait ParseMatchPat: Sized {
|
||||||
Pat::Ident(PatIdent {
|
Pat::Ident(PatIdent {
|
||||||
attrs: _,
|
attrs: _,
|
||||||
by_ref,
|
by_ref,
|
||||||
mut mutability,
|
mutability,
|
||||||
ident,
|
ident,
|
||||||
subpat,
|
subpat,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -469,13 +459,10 @@ trait ParseMatchPat: Sized {
|
||||||
.errors
|
.errors
|
||||||
.error(by_ref, "ref not allowed in #[hdl] patterns");
|
.error(by_ref, "ref not allowed in #[hdl] patterns");
|
||||||
}
|
}
|
||||||
if let Some(mut_token) = mutability {
|
if let Some(mutability) = mutability {
|
||||||
if state.sim.is_none() {
|
|
||||||
state
|
state
|
||||||
.errors
|
.errors
|
||||||
.error(mut_token, "mut not allowed in #[hdl] patterns");
|
.error(mutability, "mut not allowed in #[hdl] patterns");
|
||||||
mutability = None; // avoid duplicate errors
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some((at_token, _)) = subpat {
|
if let Some((at_token, _)) = subpat {
|
||||||
state
|
state
|
||||||
|
|
@ -487,13 +474,7 @@ trait ParseMatchPat: Sized {
|
||||||
variant_path,
|
variant_path,
|
||||||
enum_path,
|
enum_path,
|
||||||
variant_name,
|
variant_name,
|
||||||
}) => {
|
}) => Self::enum_variant(
|
||||||
if let Some(mut_token) = mutability {
|
|
||||||
state
|
|
||||||
.errors
|
|
||||||
.error(mut_token, "mut not allowed on unit variants");
|
|
||||||
}
|
|
||||||
Self::enum_variant(
|
|
||||||
state,
|
state,
|
||||||
MatchPatEnumVariant {
|
MatchPatEnumVariant {
|
||||||
match_span: state.match_span,
|
match_span: state.match_span,
|
||||||
|
|
@ -503,10 +484,8 @@ trait ParseMatchPat: Sized {
|
||||||
variant_name,
|
variant_name,
|
||||||
field: None,
|
field: None,
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
}
|
|
||||||
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
|
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
|
||||||
mutability,
|
|
||||||
ident,
|
ident,
|
||||||
}))),
|
}))),
|
||||||
}
|
}
|
||||||
|
|
@ -1001,16 +980,15 @@ struct HdlMatchParseState<'a> {
|
||||||
|
|
||||||
struct HdlLetPatVisitState<'a> {
|
struct HdlLetPatVisitState<'a> {
|
||||||
errors: &'a mut Errors,
|
errors: &'a mut Errors,
|
||||||
bindings: BTreeMap<Ident, MatchPatBinding>,
|
bindings: BTreeSet<&'a Ident>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
||||||
fn visit_match_pat_binding(&mut self, v: &'a mut MatchPatBinding) {
|
fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) {
|
||||||
self.bindings.insert(v.ident.clone(), v.clone());
|
self.bindings.insert(&v.ident);
|
||||||
v.mutability = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_match_pat_or(&mut self, v: &'a mut MatchPatOr<MatchPat>) {
|
fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) {
|
||||||
if let Some(first_inner_vert) = v.first_inner_vert() {
|
if let Some(first_inner_vert) = v.first_inner_vert() {
|
||||||
self.errors.error(
|
self.errors.error(
|
||||||
first_inner_vert,
|
first_inner_vert,
|
||||||
|
|
@ -1020,7 +998,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
||||||
visit_match_pat_or(self, v);
|
visit_match_pat_or(self, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_match_pat_or_simple(&mut self, v: &'a mut MatchPatOr<MatchPatSimple>) {
|
fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) {
|
||||||
if let Some(first_inner_vert) = v.first_inner_vert() {
|
if let Some(first_inner_vert) = v.first_inner_vert() {
|
||||||
self.errors.error(
|
self.errors.error(
|
||||||
first_inner_vert,
|
first_inner_vert,
|
||||||
|
|
@ -1030,7 +1008,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
||||||
visit_match_pat_or_simple(self, v);
|
visit_match_pat_or_simple(self, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_match_pat_enum_variant(&mut self, v: &'a mut MatchPatEnumVariant) {
|
fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) {
|
||||||
self.errors.error(v, "refutable pattern in let statement");
|
self.errors.error(v, "refutable pattern in let statement");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1070,7 +1048,7 @@ impl Visitor<'_> {
|
||||||
.error(else_, "#[hdl] let ... else { ... } is not implemented");
|
.error(else_, "#[hdl] let ... else { ... } is not implemented");
|
||||||
return empty_let();
|
return empty_let();
|
||||||
}
|
}
|
||||||
let Ok(mut pat) = MatchPat::parse(
|
let Ok(pat) = MatchPat::parse(
|
||||||
&mut HdlMatchParseState {
|
&mut HdlMatchParseState {
|
||||||
sim,
|
sim,
|
||||||
match_span: span,
|
match_span: span,
|
||||||
|
|
@ -1082,25 +1060,20 @@ impl Visitor<'_> {
|
||||||
};
|
};
|
||||||
let mut state = HdlLetPatVisitState {
|
let mut state = HdlLetPatVisitState {
|
||||||
errors: &mut self.errors,
|
errors: &mut self.errors,
|
||||||
bindings: BTreeMap::new(),
|
bindings: BTreeSet::new(),
|
||||||
};
|
};
|
||||||
state.visit_match_pat(&mut pat);
|
state.visit_match_pat(&pat);
|
||||||
let HdlLetPatVisitState {
|
let HdlLetPatVisitState {
|
||||||
errors: _,
|
errors: _,
|
||||||
bindings,
|
bindings,
|
||||||
} = state;
|
} = state;
|
||||||
let bindings_idents = bindings.keys();
|
|
||||||
let bindings = bindings.values();
|
|
||||||
let retval = if sim.is_some() {
|
let retval = if sim.is_some() {
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
let (#(#bindings,)*) = {
|
let (#(#bindings,)*) = {
|
||||||
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
||||||
let __match_value = #expr;
|
let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
|
||||||
// use method syntax to deduce what type to convert to
|
#let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token
|
||||||
let __match_value = ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value)
|
(#(#bindings,)*)
|
||||||
.__fayalite_match_sim_value();
|
|
||||||
#let_token #pat #eq_token __match_value #semi_token
|
|
||||||
(#(#bindings_idents,)*)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1132,7 +1105,7 @@ impl Visitor<'_> {
|
||||||
__match_variant,
|
__match_variant,
|
||||||
);
|
);
|
||||||
#let_token #pat #eq_token __match_variant #semi_token
|
#let_token #pat #eq_token __match_variant #semi_token
|
||||||
(#(#bindings_idents,)* __scope,)
|
(#(#bindings,)* __scope,)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1169,11 +1142,8 @@ impl Visitor<'_> {
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
{
|
{
|
||||||
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
||||||
let __match_value = #expr;
|
let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
|
||||||
// use method syntax to deduce what type to convert to
|
#match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) {
|
||||||
let __match_value = ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value)
|
|
||||||
.__fayalite_match_sim_value();
|
|
||||||
#match_token __match_value {
|
|
||||||
#(#arms)*
|
#(#arms)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,9 @@ rust-version.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64.workspace = true
|
|
||||||
bitvec.workspace = true
|
bitvec.workspace = true
|
||||||
blake3.workspace = true
|
blake3.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
clap_complete.workspace = true
|
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
eyre.workspace = true
|
eyre.workspace = true
|
||||||
fayalite-proc-macros.workspace = true
|
fayalite-proc-macros.workspace = true
|
||||||
|
|
@ -26,12 +24,10 @@ hashbrown.workspace = true
|
||||||
jobslot.workspace = true
|
jobslot.workspace = true
|
||||||
num-bigint.workspace = true
|
num-bigint.workspace = true
|
||||||
num-traits.workspace = true
|
num-traits.workspace = true
|
||||||
once_cell.workspace = true
|
os_pipe.workspace = true
|
||||||
ordered-float.workspace = true
|
|
||||||
petgraph.workspace = true
|
petgraph.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
sha2.workspace = true
|
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
vec_map.workspace = true
|
vec_map.workspace = true
|
||||||
which.workspace = true
|
which.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,47 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
use fayalite::prelude::*;
|
use clap::Parser;
|
||||||
|
use fayalite::{cli, prelude::*};
|
||||||
|
|
||||||
#[hdl_module]
|
#[hdl_module]
|
||||||
fn blinky(platform_io_builder: PlatformIOBuilder<'_>) {
|
fn blinky(clock_frequency: u64) {
|
||||||
let clk_input =
|
#[hdl]
|
||||||
platform_io_builder.peripherals_with_type::<peripherals::ClockInput>()[0].use_peripheral();
|
let clk: Clock = m.input();
|
||||||
let rst = platform_io_builder.peripherals_with_type::<Reset>()[0].use_peripheral();
|
#[hdl]
|
||||||
|
let rst: SyncReset = m.input();
|
||||||
let cd = #[hdl]
|
let cd = #[hdl]
|
||||||
ClockDomain {
|
ClockDomain {
|
||||||
clk: clk_input.clk,
|
clk,
|
||||||
rst,
|
rst: rst.to_reset(),
|
||||||
};
|
};
|
||||||
let max_value = (clk_input.ty().frequency() / 2.0).round_ties_even() as u64 - 1;
|
let max_value = clock_frequency / 2 - 1;
|
||||||
let int_ty = UInt::range_inclusive(0..=max_value);
|
let int_ty = UInt::range_inclusive(0..=max_value);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty));
|
let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty));
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let output_reg: Bool = reg_builder().clock_domain(cd).reset(false);
|
let output_reg: Bool = reg_builder().clock_domain(cd).reset(false);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let rgb_output_reg = reg_builder().clock_domain(cd).reset(
|
|
||||||
#[hdl]
|
|
||||||
peripherals::RgbLed {
|
|
||||||
r: false,
|
|
||||||
g: false,
|
|
||||||
b: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
#[hdl]
|
|
||||||
if counter_reg.cmp_eq(max_value) {
|
if counter_reg.cmp_eq(max_value) {
|
||||||
connect_any(counter_reg, 0u8);
|
connect_any(counter_reg, 0u8);
|
||||||
connect(output_reg, !output_reg);
|
connect(output_reg, !output_reg);
|
||||||
connect(rgb_output_reg.r, !rgb_output_reg.r);
|
|
||||||
#[hdl]
|
|
||||||
if rgb_output_reg.r {
|
|
||||||
connect(rgb_output_reg.g, !rgb_output_reg.g);
|
|
||||||
#[hdl]
|
|
||||||
if rgb_output_reg.g {
|
|
||||||
connect(rgb_output_reg.b, !rgb_output_reg.b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
connect_any(counter_reg, counter_reg + 1_hdl_u1);
|
connect_any(counter_reg, counter_reg + 1_hdl_u1);
|
||||||
}
|
}
|
||||||
for led in platform_io_builder.peripherals_with_type::<peripherals::Led>() {
|
|
||||||
if let Ok(led) = led.try_use_peripheral() {
|
|
||||||
connect(led.on, output_reg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for rgb_led in platform_io_builder.peripherals_with_type::<peripherals::RgbLed>() {
|
|
||||||
if let Ok(rgb_led) = rgb_led.try_use_peripheral() {
|
|
||||||
connect(rgb_led, rgb_output_reg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let io = m.add_platform_io(platform_io_builder);
|
let led: Bool = m.output();
|
||||||
|
connect(led, output_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
#[derive(Parser)]
|
||||||
<BuildCli>::main("blinky", |_, platform, _| {
|
struct Cli {
|
||||||
Ok(JobParams::new(platform.wrap_main_module(blinky)))
|
/// clock frequency in hertz
|
||||||
});
|
#[arg(long, default_value = "1000000", value_parser = clap::value_parser!(u64).range(2..))]
|
||||||
|
clock_frequency: u64,
|
||||||
|
#[command(subcommand)]
|
||||||
|
cli: cli::Cli,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> cli::Result {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
cli.cli.run(blinky(cli.clock_frequency))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
use clap::builder::TypedValueParser;
|
|
||||||
use fayalite::{
|
|
||||||
build::{ToArgs, WriteArgs},
|
|
||||||
platform::PeripheralRef,
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use ordered_float::NotNan;
|
|
||||||
|
|
||||||
fn pick_clock<'a>(
|
|
||||||
platform_io_builder: &PlatformIOBuilder<'a>,
|
|
||||||
) -> PeripheralRef<'a, peripherals::ClockInput> {
|
|
||||||
let mut clks = platform_io_builder.peripherals_with_type::<peripherals::ClockInput>();
|
|
||||||
clks.sort_by_key(|clk| {
|
|
||||||
// sort clocks by preference, smaller return values means higher preference
|
|
||||||
let mut frequency = clk.ty().frequency();
|
|
||||||
let priority;
|
|
||||||
if frequency < 10e6 {
|
|
||||||
frequency = -frequency; // prefer bigger frequencies
|
|
||||||
priority = 1;
|
|
||||||
} else if frequency > 50e6 {
|
|
||||||
// prefer smaller frequencies
|
|
||||||
priority = 2; // least preferred
|
|
||||||
} else {
|
|
||||||
priority = 0; // most preferred
|
|
||||||
frequency = (frequency - 25e6).abs(); // prefer closer to 25MHz
|
|
||||||
}
|
|
||||||
(priority, NotNan::new(frequency).expect("should be valid"))
|
|
||||||
});
|
|
||||||
clks[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl_module]
|
|
||||||
fn tx_only_uart(
|
|
||||||
platform_io_builder: PlatformIOBuilder<'_>,
|
|
||||||
divisor: f64,
|
|
||||||
message: impl AsRef<[u8]>,
|
|
||||||
) {
|
|
||||||
let message = message.as_ref();
|
|
||||||
let clk_input = pick_clock(&platform_io_builder).use_peripheral();
|
|
||||||
let rst = platform_io_builder.peripherals_with_type::<Reset>()[0].use_peripheral();
|
|
||||||
let cd = #[hdl]
|
|
||||||
ClockDomain {
|
|
||||||
clk: clk_input.clk,
|
|
||||||
rst,
|
|
||||||
};
|
|
||||||
let numerator = 1u128 << 16;
|
|
||||||
let denominator = (divisor * numerator as f64).round() as u128;
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let remainder_reg: UInt<128> = reg_builder().clock_domain(cd).reset(0u128);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let sum: UInt<128> = wire();
|
|
||||||
connect_any(sum, remainder_reg + numerator);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let tick_reg = reg_builder().clock_domain(cd).reset(false);
|
|
||||||
connect(tick_reg, false);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let next_remainder: UInt<128> = wire();
|
|
||||||
connect(remainder_reg, next_remainder);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
if sum.cmp_ge(denominator) {
|
|
||||||
connect_any(next_remainder, sum - denominator);
|
|
||||||
connect(tick_reg, true);
|
|
||||||
} else {
|
|
||||||
connect(next_remainder, sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let uart_state_reg = reg_builder().clock_domain(cd).reset(0_hdl_u4);
|
|
||||||
#[hdl]
|
|
||||||
let next_uart_state: UInt<4> = wire();
|
|
||||||
|
|
||||||
connect_any(next_uart_state, uart_state_reg + 1u8);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let message_mem: Array<UInt<8>> = wire(Array[UInt::new_static()][message.len()]);
|
|
||||||
for (message, message_mem) in message.iter().zip(message_mem) {
|
|
||||||
connect(message_mem, *message);
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
let addr_reg: UInt<32> = reg_builder().clock_domain(cd).reset(0u32);
|
|
||||||
#[hdl]
|
|
||||||
let next_addr: UInt<32> = wire();
|
|
||||||
connect(next_addr, addr_reg);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let tx = reg_builder().clock_domain(cd).reset(true);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let tx_bits: Array<Bool, 10> = wire();
|
|
||||||
|
|
||||||
connect(tx_bits[0], false); // start bit
|
|
||||||
connect(tx_bits[9], true); // stop bit
|
|
||||||
|
|
||||||
for i in 0..8 {
|
|
||||||
connect(tx_bits[i + 1], message_mem[addr_reg][i]); // data bits
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(tx, tx_bits[uart_state_reg]);
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
if uart_state_reg.cmp_eq(tx_bits.ty().len() - 1) {
|
|
||||||
connect(next_uart_state, 0_hdl_u4);
|
|
||||||
let next_addr_val = addr_reg + 1u8;
|
|
||||||
#[hdl]
|
|
||||||
if next_addr_val.cmp_lt(message.len()) {
|
|
||||||
connect_any(next_addr, next_addr_val);
|
|
||||||
} else {
|
|
||||||
connect(next_addr, 0u32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
if tick_reg {
|
|
||||||
connect(uart_state_reg, next_uart_state);
|
|
||||||
connect(addr_reg, next_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for uart in platform_io_builder.peripherals_with_type::<peripherals::Uart>() {
|
|
||||||
connect(uart.use_peripheral().tx, tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let io = m.add_platform_io(platform_io_builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_baud_rate(
|
|
||||||
v: impl AsRef<str>,
|
|
||||||
) -> Result<NotNan<f64>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let retval: NotNan<f64> = v
|
|
||||||
.as_ref()
|
|
||||||
.parse()
|
|
||||||
.map_err(|_| "invalid baud rate, must be a finite positive floating-point value")?;
|
|
||||||
if *retval > 0.0 && retval.is_finite() {
|
|
||||||
Ok(retval)
|
|
||||||
} else {
|
|
||||||
Err("baud rate must be finite and positive".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
|
|
||||||
pub struct ExtraArgs {
|
|
||||||
#[arg(long, value_parser = clap::builder::StringValueParser::new().try_map(parse_baud_rate), default_value = "115200")]
|
|
||||||
pub baud_rate: NotNan<f64>,
|
|
||||||
#[arg(long, default_value = "Hello World from Fayalite!!!\r\n", value_parser = clap::builder::NonEmptyStringValueParser::new())]
|
|
||||||
pub message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToArgs for ExtraArgs {
|
|
||||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
|
||||||
let Self { baud_rate, message } = self;
|
|
||||||
args.write_display_arg(format_args!("--baud-rate={baud_rate}"));
|
|
||||||
args.write_long_option_eq("message", message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
type Cli = BuildCli<ExtraArgs>;
|
|
||||||
Cli::main(
|
|
||||||
"tx_only_uart",
|
|
||||||
|_, platform, ExtraArgs { baud_rate, message }| {
|
|
||||||
Ok(JobParams::new(platform.try_wrap_main_module(|io| {
|
|
||||||
let clk = pick_clock(&io).ty();
|
|
||||||
let divisor = clk.frequency() / *baud_rate;
|
|
||||||
let baud_rate_error = |msg| {
|
|
||||||
<Cli as clap::CommandFactory>::command()
|
|
||||||
.error(clap::error::ErrorKind::ValueValidation, msg)
|
|
||||||
};
|
|
||||||
const HUGE_DIVISOR: f64 = u64::MAX as f64;
|
|
||||||
match divisor {
|
|
||||||
divisor if !divisor.is_finite() => {
|
|
||||||
return Err(baud_rate_error("bad baud rate"));
|
|
||||||
}
|
|
||||||
HUGE_DIVISOR.. => return Err(baud_rate_error("baud rate is too small")),
|
|
||||||
4.0.. => {}
|
|
||||||
_ => return Err(baud_rate_error("baud rate is too large")),
|
|
||||||
}
|
|
||||||
Ok(tx_only_uart(io, divisor, message))
|
|
||||||
})?))
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -31,114 +31,3 @@
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
|
||||||
//! You can also use `#[hdl(sim)] let` to destructure [`SimValue`]s (or anything that implements [`ToSimValue`]).
|
|
||||||
//!
|
|
||||||
//! [`SimValue`]: crate::sim::value::SimValue
|
|
||||||
//! [`ToSimValue`]: crate::sim::value::ToSimValue
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! # use fayalite::prelude::*;
|
|
||||||
//! #[hdl]
|
|
||||||
//! struct MyStruct<T> {
|
|
||||||
//! a: UInt<8>,
|
|
||||||
//! b: Bool,
|
|
||||||
//! c: T,
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn destructure<T: Type>(v: SimValue<MyStruct<T>>) {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! let MyStruct::<T> {
|
|
||||||
//! a,
|
|
||||||
//! mut b,
|
|
||||||
//! c,
|
|
||||||
//! } = v;
|
|
||||||
//!
|
|
||||||
//! // that gives these types:
|
|
||||||
//! let _: SimValue<UInt<8>> = a;
|
|
||||||
//! let _: SimValue<Bool> = b;
|
|
||||||
//! let _: SimValue<T> = c;
|
|
||||||
//! *b = false; // can modify b since mut was used
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn destructure_ref<'a, T: Type>(v: &'a SimValue<MyStruct<T>>) {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! let MyStruct::<T> {
|
|
||||||
//! a,
|
|
||||||
//! b,
|
|
||||||
//! c,
|
|
||||||
//! } = v;
|
|
||||||
//!
|
|
||||||
//! // that gives these types:
|
|
||||||
//! let _: &'a SimValue<UInt<8>> = a;
|
|
||||||
//! let _: &'a SimValue<Bool> = b;
|
|
||||||
//! let _: &'a SimValue<T> = c;
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn destructure_mut<'a, T: Type>(v: &'a mut SimValue<MyStruct<T>>) {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! let MyStruct::<T> {
|
|
||||||
//! a,
|
|
||||||
//! b,
|
|
||||||
//! c,
|
|
||||||
//! } = v;
|
|
||||||
//!
|
|
||||||
//! **b = true; // you can modify v by modifying b which borrows from it
|
|
||||||
//!
|
|
||||||
//! // that gives these types:
|
|
||||||
//! let _: &'a mut SimValue<UInt<8>> = a;
|
|
||||||
//! let _: &'a mut SimValue<Bool> = b;
|
|
||||||
//! let _: &'a mut SimValue<T> = c;
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn destructure_inner<T: Type>(v: <MyStruct<T> as Type>::SimValue) {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! let MyStruct::<T> {
|
|
||||||
//! a,
|
|
||||||
//! mut b,
|
|
||||||
//! c,
|
|
||||||
//! } = v;
|
|
||||||
//!
|
|
||||||
//! // that gives these types:
|
|
||||||
//! let _: SimValue<UInt<8>> = a;
|
|
||||||
//! let _: SimValue<Bool> = b;
|
|
||||||
//! let _: SimValue<T> = c;
|
|
||||||
//! *b = false; // can modify b since mut was used
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn destructure_inner_ref<'a, T: Type>(v: &'a <MyStruct<T> as Type>::SimValue) {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! let MyStruct::<T> {
|
|
||||||
//! a,
|
|
||||||
//! b,
|
|
||||||
//! c,
|
|
||||||
//! } = v;
|
|
||||||
//!
|
|
||||||
//! // that gives these types:
|
|
||||||
//! let _: &'a SimValue<UInt<8>> = a;
|
|
||||||
//! let _: &'a SimValue<Bool> = b;
|
|
||||||
//! let _: &'a SimValue<T> = c;
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn destructure_inner_mut<'a, T: Type>(v: &'a mut <MyStruct<T> as Type>::SimValue) {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! let MyStruct::<T> {
|
|
||||||
//! a,
|
|
||||||
//! b,
|
|
||||||
//! c,
|
|
||||||
//! } = v;
|
|
||||||
//!
|
|
||||||
//! **b = true; // you can modify v by modifying b which borrows from it
|
|
||||||
//!
|
|
||||||
//! // that gives these types:
|
|
||||||
//! let _: &'a mut SimValue<UInt<8>> = a;
|
|
||||||
//! let _: &'a mut SimValue<Bool> = b;
|
|
||||||
//! let _: &'a mut SimValue<T> = c;
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
|
|
|
||||||
|
|
@ -9,110 +9,3 @@
|
||||||
//!
|
//!
|
||||||
//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now,
|
//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now,
|
||||||
//! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
|
//! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
|
||||||
//!
|
|
||||||
//! You can also use `#[hdl(sim)] match` to match [`SimValue`]s (or anything that implements [`ToSimValue`]).
|
|
||||||
//!
|
|
||||||
//! `#[hdl(sim)] match` statements' bodies may have any type, unlike `#[hdl] match`.
|
|
||||||
//!
|
|
||||||
//! [`SimValue`]: crate::sim::value::SimValue
|
|
||||||
//! [`ToSimValue`]: crate::sim::value::ToSimValue
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! # use fayalite::prelude::*;
|
|
||||||
//! #[hdl]
|
|
||||||
//! enum MyEnum<T> {
|
|
||||||
//! A,
|
|
||||||
//! B(Bool),
|
|
||||||
//! C(T),
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn match_move<T: Type>(v: SimValue<MyEnum<T>>) -> String {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! match v {
|
|
||||||
//! MyEnum::<T>::A => String::from("got A"),
|
|
||||||
//! MyEnum::<T>::B(mut b) => {
|
|
||||||
//! let _: SimValue<Bool> = b; // b has this type
|
|
||||||
//! let text = format!("got B({b})");
|
|
||||||
//! *b = true; // can modify b since mut was used
|
|
||||||
//! text
|
|
||||||
//! }
|
|
||||||
//! _ => String::from("something else"),
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn match_ref<'a, T: Type>(v: &'a SimValue<MyEnum<T>>) -> u32 {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! match v {
|
|
||||||
//! MyEnum::<T>::A => 1,
|
|
||||||
//! MyEnum::<T>::B(b) => {
|
|
||||||
//! let _: &'a SimValue<Bool> = b; // b has this type
|
|
||||||
//! println!("got B({b})");
|
|
||||||
//! 5
|
|
||||||
//! }
|
|
||||||
//! _ => 42,
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn match_mut<'a, T: Type>(v: &'a mut SimValue<MyEnum<T>>) -> Option<&'a mut SimValue<T>> {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! match v {
|
|
||||||
//! MyEnum::<T>::A => None,
|
|
||||||
//! MyEnum::<T>::B(b) => {
|
|
||||||
//! println!("got B({b})");
|
|
||||||
//! **b = true; // you can modify v by modifying b which borrows from it
|
|
||||||
//! let _: &'a mut SimValue<Bool> = b; // b has this type
|
|
||||||
//! None
|
|
||||||
//! }
|
|
||||||
//! MyEnum::<T>::C(v) => Some(v), // you can return matched values
|
|
||||||
//! _ => None, // HDL enums can have invalid discriminants, so we need this extra match arm
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn match_inner_move<T: Type>(v: <MyEnum<T> as Type>::SimValue) -> String {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! match v {
|
|
||||||
//! MyEnum::<T>::A => String::from("got A"),
|
|
||||||
//! MyEnum::<T>::B(mut b) => {
|
|
||||||
//! let _: SimValue<Bool> = b; // b has this type
|
|
||||||
//! let text = format!("got B({b})");
|
|
||||||
//! *b = true; // can modify b since mut was used
|
|
||||||
//! text
|
|
||||||
//! }
|
|
||||||
//! _ => String::from("something else"),
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn match_inner_ref<'a, T: Type>(v: &'a <MyEnum<T> as Type>::SimValue) -> u32 {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! match v {
|
|
||||||
//! MyEnum::<T>::A => 1,
|
|
||||||
//! MyEnum::<T>::B(b) => {
|
|
||||||
//! let _: &'a SimValue<Bool> = b; // b has this type
|
|
||||||
//! println!("got B({b})");
|
|
||||||
//! 5
|
|
||||||
//! }
|
|
||||||
//! _ => 42,
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[hdl]
|
|
||||||
//! fn match_inner_mut<'a, T: Type>(v: &'a mut <MyEnum<T> as Type>::SimValue) -> Option<&'a mut SimValue<T>> {
|
|
||||||
//! #[hdl(sim)]
|
|
||||||
//! match v {
|
|
||||||
//! MyEnum::<T>::A => None,
|
|
||||||
//! MyEnum::<T>::B(b) => {
|
|
||||||
//! println!("got B({b})");
|
|
||||||
//! **b = true; // you can modify v by modifying b which borrows from it
|
|
||||||
//! let _: &'a mut SimValue<Bool> = b; // b has this type
|
|
||||||
//! None
|
|
||||||
//! }
|
|
||||||
//! MyEnum::<T>::C(v) => Some(v), // you can return matched values
|
|
||||||
//! _ => None, // HDL enums can have invalid discriminants, so we need this extra match arm
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
|
|
|
||||||
|
|
@ -145,73 +145,52 @@ pub struct DocStringAnnotation {
|
||||||
|
|
||||||
macro_rules! make_annotation_enum {
|
macro_rules! make_annotation_enum {
|
||||||
(
|
(
|
||||||
#[$non_exhaustive:ident]
|
|
||||||
$(#[$meta:meta])*
|
$(#[$meta:meta])*
|
||||||
$vis:vis enum $AnnotationEnum:ident {
|
$vis:vis enum $Annotation:ident {
|
||||||
$($Variant:ident($T:ty),)*
|
$($Variant:ident($T:ident),)*
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
crate::annotations::make_annotation_enum!(@require_non_exhaustive $non_exhaustive);
|
|
||||||
|
|
||||||
#[$non_exhaustive]
|
|
||||||
$(#[$meta])*
|
$(#[$meta])*
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
$vis enum $Annotation {
|
||||||
$vis enum $AnnotationEnum {
|
|
||||||
$($Variant($T),)*
|
$($Variant($T),)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for $AnnotationEnum {
|
$(impl IntoAnnotations for $T {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
type IntoAnnotations = [$Annotation; 1];
|
||||||
match self {
|
|
||||||
$(Self::$Variant(v) => v.fmt(f),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(impl From<$T> for crate::annotations::Annotation {
|
|
||||||
fn from(v: $T) -> Self {
|
|
||||||
$AnnotationEnum::$Variant(v).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::annotations::IntoAnnotations for $T {
|
|
||||||
type IntoAnnotations = [crate::annotations::Annotation; 1];
|
|
||||||
|
|
||||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||||
[self.into()]
|
[$Annotation::$Variant(self)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::annotations::IntoAnnotations for &'_ $T {
|
impl IntoAnnotations for &'_ $T {
|
||||||
type IntoAnnotations = [crate::annotations::Annotation; 1];
|
type IntoAnnotations = [$Annotation; 1];
|
||||||
|
|
||||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||||
[crate::annotations::Annotation::from(self.clone())]
|
[$Annotation::$Variant(*self)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::annotations::IntoAnnotations for &'_ mut $T {
|
impl IntoAnnotations for &'_ mut $T {
|
||||||
type IntoAnnotations = [crate::annotations::Annotation; 1];
|
type IntoAnnotations = [$Annotation; 1];
|
||||||
|
|
||||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||||
[crate::annotations::Annotation::from(self.clone())]
|
[$Annotation::$Variant(*self)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::annotations::IntoAnnotations for Box<$T> {
|
impl IntoAnnotations for Box<$T> {
|
||||||
type IntoAnnotations = [crate::annotations::Annotation; 1];
|
type IntoAnnotations = [$Annotation; 1];
|
||||||
|
|
||||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||||
[crate::annotations::Annotation::from(*self)]
|
[$Annotation::$Variant(*self)]
|
||||||
}
|
}
|
||||||
})*
|
})*
|
||||||
};
|
};
|
||||||
(@require_non_exhaustive non_exhaustive) => {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use make_annotation_enum;
|
|
||||||
|
|
||||||
make_annotation_enum! {
|
make_annotation_enum! {
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Annotation {
|
pub enum Annotation {
|
||||||
DontTouch(DontTouchAnnotation),
|
DontTouch(DontTouchAnnotation),
|
||||||
|
|
@ -220,7 +199,6 @@ make_annotation_enum! {
|
||||||
BlackBoxPath(BlackBoxPathAnnotation),
|
BlackBoxPath(BlackBoxPathAnnotation),
|
||||||
DocString(DocStringAnnotation),
|
DocString(DocStringAnnotation),
|
||||||
CustomFirrtl(CustomFirrtlAnnotation),
|
CustomFirrtl(CustomFirrtlAnnotation),
|
||||||
Xilinx(crate::vendor::xilinx::XilinxAnnotation),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,10 +216,7 @@ impl TargetedAnnotation {
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn assert_valid_target(target: Interned<Target>) {
|
pub fn assert_valid_target(target: Interned<Target>) {
|
||||||
assert!(
|
assert!(target.is_static(), "can't annotate non-static targets");
|
||||||
target.is_valid_annotation_target(),
|
|
||||||
"not a valid annotation target: {target:?}",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
pub fn target(&self) -> Interned<Target> {
|
pub fn target(&self) -> Interned<Target> {
|
||||||
self.target
|
self.target
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,23 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{
|
expr::{
|
||||||
CastToBits, Expr, HdlPartialEq, HdlPartialEqImpl, ReduceBits, ToExpr, ValueType, Valueless,
|
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
|
||||||
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, StructuralEq},
|
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq},
|
||||||
},
|
},
|
||||||
int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType},
|
int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType},
|
||||||
intern::{Intern, Interned, LazyInterned},
|
intern::{Intern, Interned, LazyInterned},
|
||||||
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
||||||
sim::value::SimValue,
|
sim::value::{SimValue, SimValuePartialEq},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{
|
ty::{
|
||||||
CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
||||||
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties, TypeWithDeref,
|
OpaqueSimValueWritten, StaticType, Type, TypeProperties, TypeWithDeref,
|
||||||
serde_impls::SerdeCanonicalType,
|
serde_impls::SerdeCanonicalType,
|
||||||
},
|
},
|
||||||
util::ConstUsize,
|
util::ConstUsize,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
|
||||||
use std::{borrow::Cow, fmt, iter::FusedIterator, ops::Index};
|
use std::{iter::FusedIterator, ops::Index};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
||||||
|
|
@ -28,8 +28,8 @@ pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
||||||
type_properties: TypeProperties,
|
type_properties: TypeProperties,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type, Len: Size> fmt::Debug for ArrayType<T, Len> {
|
impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "Array<{:?}, {}>", self.element, self.len())
|
write!(f, "Array<{:?}, {}>", self.element, self.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,42 +109,14 @@ impl<T: StaticType, Len: KnownSize> Default for ArrayType<T, Len> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MakeType<T: StaticType>(Interned<T>);
|
|
||||||
|
|
||||||
impl<T: StaticType> From<MakeType<T>> for Interned<T> {
|
|
||||||
fn from(value: MakeType<T>) -> Self {
|
|
||||||
value.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: StaticType> Default for MakeType<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(T::TYPE.intern_sized())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MakeMaskType<T: StaticType>(Interned<T::MaskType>);
|
|
||||||
|
|
||||||
impl<T: StaticType> From<MakeMaskType<T>> for Interned<T::MaskType> {
|
|
||||||
fn from(value: MakeMaskType<T>) -> Self {
|
|
||||||
value.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: StaticType> Default for MakeMaskType<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(T::MASK_TYPE.intern_sized())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
|
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
|
||||||
const TYPE: Self = Self {
|
const TYPE: Self = Self {
|
||||||
element: LazyInterned::new_const::<MakeType<T>>(),
|
element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
|
||||||
len: Len::SIZE,
|
len: Len::SIZE,
|
||||||
type_properties: Self::TYPE_PROPERTIES,
|
type_properties: Self::TYPE_PROPERTIES,
|
||||||
};
|
};
|
||||||
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
|
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
|
||||||
element: LazyInterned::new_const::<MakeMaskType<T>>(),
|
element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
|
||||||
len: Len::SIZE,
|
len: Len::SIZE,
|
||||||
type_properties: Self::MASK_TYPE_PROPERTIES,
|
type_properties: Self::MASK_TYPE_PROPERTIES,
|
||||||
};
|
};
|
||||||
|
|
@ -182,15 +154,6 @@ impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type, Len: Size> SimValueDebug for ArrayType<T, Len> {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||||
type BaseType = Array;
|
type BaseType = Array;
|
||||||
type MaskType = ArrayType<T::MaskType, Len>;
|
type MaskType = ArrayType<T::MaskType, Len>;
|
||||||
|
|
@ -258,7 +221,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||||
let value = AsMut::<[SimValue<T>]>::as_mut(value);
|
let value = AsMut::<[SimValue<T>]>::as_mut(value);
|
||||||
assert_eq!(self.len(), value.len());
|
assert_eq!(self.len(), value.len());
|
||||||
for element_value in value {
|
for element_value in value {
|
||||||
assert_eq!(element_value.ty(), element_ty);
|
assert_eq!(SimValue::ty(element_value), element_ty);
|
||||||
let (element_opaque, rest) = opaque.split_at(element_size);
|
let (element_opaque, rest) = opaque.split_at(element_size);
|
||||||
SimValue::opaque_mut(element_value).clone_from_slice(element_opaque);
|
SimValue::opaque_mut(element_value).clone_from_slice(element_opaque);
|
||||||
opaque = rest;
|
opaque = rest;
|
||||||
|
|
@ -275,7 +238,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||||
let value = AsRef::<[SimValue<T>]>::as_ref(value);
|
let value = AsRef::<[SimValue<T>]>::as_ref(value);
|
||||||
assert_eq!(self.len(), value.len());
|
assert_eq!(self.len(), value.len());
|
||||||
for element_value in value {
|
for element_value in value {
|
||||||
assert_eq!(element_value.ty(), element_ty);
|
assert_eq!(SimValue::ty(element_value), element_ty);
|
||||||
writer.fill_prefix_with(element_size, |writer| {
|
writer.fill_prefix_with(element_size, |writer| {
|
||||||
writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice())
|
writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice())
|
||||||
});
|
});
|
||||||
|
|
@ -363,37 +326,14 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Lhs: Type, Rhs: Type, Len: Size> HdlPartialEqImpl<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
||||||
where
|
where
|
||||||
Lhs: HdlPartialEqImpl<Rhs>,
|
Lhs: ExprPartialEq<Rhs>,
|
||||||
{
|
{
|
||||||
const TRY_STRUCTURAL_EQ: bool = <Lhs as HdlPartialEqImpl<Rhs>>::TRY_STRUCTURAL_EQ;
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||||
|
let lhs_ty = Expr::ty(lhs);
|
||||||
fn cmp_value_eq(
|
let rhs_ty = Expr::ty(rhs);
|
||||||
lhs: Self,
|
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
rhs: ArrayType<Rhs, Len>,
|
|
||||||
rhs_value: Cow<'_, <ArrayType<Rhs, Len> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
assert_eq!(lhs.len(), rhs.len());
|
|
||||||
let lhs = lhs.element();
|
|
||||||
let rhs = rhs.element();
|
|
||||||
let lhs_value: &[_] = (*lhs_value).as_ref();
|
|
||||||
let rhs_value: &[_] = (*rhs_value).as_ref();
|
|
||||||
for (lhs_value, rhs_value) in lhs_value.iter().zip(rhs_value) {
|
|
||||||
if !Lhs::cmp_value_eq(lhs, Cow::Borrowed(lhs_value), rhs, Cow::Borrowed(rhs_value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
|
||||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
|
||||||
if Self::TRY_STRUCTURAL_EQ {
|
|
||||||
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
|
|
||||||
return retval.to_expr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lhs.into_iter()
|
lhs.into_iter()
|
||||||
.zip(rhs)
|
.zip(rhs)
|
||||||
.map(|(l, r)| l.cmp_eq(r))
|
.map(|(l, r)| l.cmp_eq(r))
|
||||||
|
|
@ -401,13 +341,11 @@ where
|
||||||
.cast_to_bits()
|
.cast_to_bits()
|
||||||
.all_one_bits()
|
.all_one_bits()
|
||||||
}
|
}
|
||||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
|
||||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||||
if Self::TRY_STRUCTURAL_EQ {
|
let lhs_ty = Expr::ty(lhs);
|
||||||
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
|
let rhs_ty = Expr::ty(rhs);
|
||||||
return !retval.to_expr();
|
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||||
}
|
|
||||||
}
|
|
||||||
lhs.into_iter()
|
lhs.into_iter()
|
||||||
.zip(rhs)
|
.zip(rhs)
|
||||||
.map(|(l, r)| l.cmp_ne(r))
|
.map(|(l, r)| l.cmp_ne(r))
|
||||||
|
|
@ -415,19 +353,17 @@ where
|
||||||
.cast_to_bits()
|
.cast_to_bits()
|
||||||
.any_one_bits()
|
.any_one_bits()
|
||||||
}
|
}
|
||||||
fn cmp_valueless_eq(
|
}
|
||||||
lhs: Valueless<Self>,
|
|
||||||
rhs: Valueless<ArrayType<Rhs, Len>>,
|
impl<Lhs: Type, Rhs: Type, Len: Size> SimValuePartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
||||||
) -> Valueless<Bool> {
|
where
|
||||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
Lhs: SimValuePartialEq<Rhs>,
|
||||||
Valueless::new(Bool)
|
{
|
||||||
}
|
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<ArrayType<Rhs, Len>>) -> bool {
|
||||||
fn cmp_valueless_ne(
|
AsRef::<[_]>::as_ref(&**this)
|
||||||
lhs: Valueless<Self>,
|
.iter()
|
||||||
rhs: Valueless<ArrayType<Rhs, Len>>,
|
.zip(AsRef::<[_]>::as_ref(&**other))
|
||||||
) -> Valueless<Bool> {
|
.all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r))
|
||||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
|
||||||
Valueless::new(Bool)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,7 +374,7 @@ impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> {
|
||||||
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter {
|
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter {
|
||||||
ExprArrayIter {
|
ExprArrayIter {
|
||||||
base: e,
|
base: e,
|
||||||
indexes: 0..e.ty().len(),
|
indexes: 0..Expr::ty(e).len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,128 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
build::{
|
|
||||||
BaseJob, BaseJobKind, CommandParams, DynJobKind, GlobalParams, JobAndDependencies,
|
|
||||||
JobArgsAndDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams,
|
|
||||||
ToArgs, WriteArgs,
|
|
||||||
},
|
|
||||||
firrtl::{ExportOptions, FileBackend},
|
|
||||||
intern::{Intern, InternSlice, Interned},
|
|
||||||
util::job_server::AcquiredJob,
|
|
||||||
};
|
|
||||||
use clap::Args;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub struct FirrtlJobKind;
|
|
||||||
|
|
||||||
#[derive(Args, Debug, Clone, Hash, PartialEq, Eq)]
|
|
||||||
#[group(id = "Firrtl")]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct FirrtlArgs {
|
|
||||||
#[command(flatten)]
|
|
||||||
pub export_options: ExportOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToArgs for FirrtlArgs {
|
|
||||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
|
||||||
let Self { export_options } = self;
|
|
||||||
export_options.to_args(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
pub struct Firrtl {
|
|
||||||
base: BaseJob,
|
|
||||||
export_options: ExportOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Firrtl {
|
|
||||||
fn make_firrtl_file_backend(&self) -> FileBackend {
|
|
||||||
FileBackend {
|
|
||||||
dir_path: PathBuf::from(&*self.base.output_dir()),
|
|
||||||
top_fir_file_stem: Some(self.base.file_stem().into()),
|
|
||||||
circuit_name: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn firrtl_file(&self) -> Interned<Path> {
|
|
||||||
self.base.file_with_ext("fir")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobKind for FirrtlJobKind {
|
|
||||||
type Args = FirrtlArgs;
|
|
||||||
type Job = Firrtl;
|
|
||||||
type Dependencies = JobKindAndDependencies<BaseJobKind>;
|
|
||||||
|
|
||||||
fn dependencies(self) -> Self::Dependencies {
|
|
||||||
JobKindAndDependencies::new(BaseJobKind)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn args_to_jobs(
|
|
||||||
args: JobArgsAndDependencies<Self>,
|
|
||||||
params: &JobParams,
|
|
||||||
global_params: &GlobalParams,
|
|
||||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
|
||||||
args.args_to_jobs_simple(
|
|
||||||
params,
|
|
||||||
global_params,
|
|
||||||
|_kind, FirrtlArgs { export_options }, dependencies| {
|
|
||||||
Ok(Firrtl {
|
|
||||||
base: dependencies.get_job::<BaseJob, _>().clone(),
|
|
||||||
export_options,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
|
|
||||||
[JobItemName::Path {
|
|
||||||
path: job.base.output_dir(),
|
|
||||||
}]
|
|
||||||
.intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
|
|
||||||
[JobItemName::Path {
|
|
||||||
path: job.firrtl_file(),
|
|
||||||
}]
|
|
||||||
.intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(self) -> Interned<str> {
|
|
||||||
"firrtl".intern()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
self,
|
|
||||||
job: &Self::Job,
|
|
||||||
inputs: &[JobItem],
|
|
||||||
params: &JobParams,
|
|
||||||
_global_params: &GlobalParams,
|
|
||||||
_acquired_job: &mut AcquiredJob,
|
|
||||||
) -> eyre::Result<Vec<JobItem>> {
|
|
||||||
let [JobItem::Path { path: input_path }] = *inputs else {
|
|
||||||
panic!("wrong inputs, expected a single `Path`");
|
|
||||||
};
|
|
||||||
assert_eq!(input_path, job.base.output_dir());
|
|
||||||
crate::firrtl::export(
|
|
||||||
job.make_firrtl_file_backend(),
|
|
||||||
params.main_module(),
|
|
||||||
job.export_options,
|
|
||||||
)?;
|
|
||||||
Ok(vec![JobItem::Path {
|
|
||||||
path: job.firrtl_file(),
|
|
||||||
}])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
|
|
||||||
[DynJobKind::new(FirrtlJobKind)]
|
|
||||||
}
|
|
||||||
|
|
@ -1,388 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
build::{
|
|
||||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams,
|
|
||||||
JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
|
|
||||||
JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
|
||||||
external::{
|
|
||||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
|
||||||
},
|
|
||||||
verilog::{UnadjustedVerilog, VerilogDialect, VerilogJob, VerilogJobKind},
|
|
||||||
},
|
|
||||||
intern::{Intern, InternSlice, Interned},
|
|
||||||
module::NameId,
|
|
||||||
testing::FormalMode,
|
|
||||||
util::job_server::AcquiredJob,
|
|
||||||
};
|
|
||||||
use clap::Args;
|
|
||||||
use eyre::Context;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::{
|
|
||||||
ffi::{OsStr, OsString},
|
|
||||||
fmt::{self, Write},
|
|
||||||
path::Path,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Args, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct FormalArgs {
|
|
||||||
#[arg(long = "sby-extra-arg", value_name = "ARG")]
|
|
||||||
pub sby_extra_args: Vec<OsString>,
|
|
||||||
#[arg(long, default_value_t)]
|
|
||||||
pub formal_mode: FormalMode,
|
|
||||||
#[arg(long, default_value_t = Self::DEFAULT_DEPTH)]
|
|
||||||
pub formal_depth: u64,
|
|
||||||
#[arg(long, default_value = Self::DEFAULT_SOLVER)]
|
|
||||||
pub formal_solver: String,
|
|
||||||
#[arg(long = "smtbmc-extra-arg", value_name = "ARG")]
|
|
||||||
pub smtbmc_extra_args: Vec<OsString>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormalArgs {
|
|
||||||
pub const DEFAULT_DEPTH: u64 = 20;
|
|
||||||
pub const DEFAULT_SOLVER: &'static str = "z3";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToArgs for FormalArgs {
|
|
||||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
|
||||||
let Self {
|
|
||||||
sby_extra_args,
|
|
||||||
formal_mode,
|
|
||||||
formal_depth,
|
|
||||||
formal_solver,
|
|
||||||
smtbmc_extra_args,
|
|
||||||
} = self;
|
|
||||||
for arg in sby_extra_args {
|
|
||||||
args.write_long_option_eq("sby-extra-arg", arg);
|
|
||||||
}
|
|
||||||
args.write_display_args([
|
|
||||||
format_args!("--formal-mode={formal_mode}"),
|
|
||||||
format_args!("--formal-depth={formal_depth}"),
|
|
||||||
format_args!("--formal-solver={formal_solver}"),
|
|
||||||
]);
|
|
||||||
for arg in smtbmc_extra_args {
|
|
||||||
args.write_long_option_eq("smtbmc-extra-arg", arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
|
||||||
pub struct WriteSbyFileJobKind;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
|
||||||
pub struct WriteSbyFileJob {
|
|
||||||
sby_extra_args: Interned<[Interned<OsStr>]>,
|
|
||||||
formal_mode: FormalMode,
|
|
||||||
formal_depth: u64,
|
|
||||||
formal_solver: Interned<str>,
|
|
||||||
smtbmc_extra_args: Interned<[Interned<OsStr>]>,
|
|
||||||
sby_file: Interned<Path>,
|
|
||||||
output_dir: Interned<Path>,
|
|
||||||
main_verilog_file: Interned<Path>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WriteSbyFileJob {
|
|
||||||
pub fn sby_extra_args(&self) -> Interned<[Interned<OsStr>]> {
|
|
||||||
self.sby_extra_args
|
|
||||||
}
|
|
||||||
pub fn formal_mode(&self) -> FormalMode {
|
|
||||||
self.formal_mode
|
|
||||||
}
|
|
||||||
pub fn formal_depth(&self) -> u64 {
|
|
||||||
self.formal_depth
|
|
||||||
}
|
|
||||||
pub fn formal_solver(&self) -> Interned<str> {
|
|
||||||
self.formal_solver
|
|
||||||
}
|
|
||||||
pub fn smtbmc_extra_args(&self) -> Interned<[Interned<OsStr>]> {
|
|
||||||
self.smtbmc_extra_args
|
|
||||||
}
|
|
||||||
pub fn sby_file(&self) -> Interned<Path> {
|
|
||||||
self.sby_file
|
|
||||||
}
|
|
||||||
pub fn output_dir(&self) -> Interned<Path> {
|
|
||||||
self.output_dir
|
|
||||||
}
|
|
||||||
pub fn main_verilog_file(&self) -> Interned<Path> {
|
|
||||||
self.main_verilog_file
|
|
||||||
}
|
|
||||||
fn write_sby(
|
|
||||||
&self,
|
|
||||||
output: &mut OsString,
|
|
||||||
additional_files: &[Interned<Path>],
|
|
||||||
main_module_name_id: NameId,
|
|
||||||
) -> eyre::Result<()> {
|
|
||||||
let Self {
|
|
||||||
sby_extra_args: _,
|
|
||||||
formal_mode,
|
|
||||||
formal_depth,
|
|
||||||
formal_solver,
|
|
||||||
smtbmc_extra_args,
|
|
||||||
sby_file: _,
|
|
||||||
output_dir: _,
|
|
||||||
main_verilog_file,
|
|
||||||
} = self;
|
|
||||||
write!(
|
|
||||||
output,
|
|
||||||
"[options]\n\
|
|
||||||
mode {formal_mode}\n\
|
|
||||||
depth {formal_depth}\n\
|
|
||||||
wait on\n\
|
|
||||||
\n\
|
|
||||||
[engines]\n\
|
|
||||||
smtbmc {formal_solver} -- --"
|
|
||||||
)
|
|
||||||
.expect("writing to OsString can't fail");
|
|
||||||
for i in smtbmc_extra_args {
|
|
||||||
output.push(" ");
|
|
||||||
output.push(i);
|
|
||||||
}
|
|
||||||
output.push(
|
|
||||||
"\n\
|
|
||||||
\n\
|
|
||||||
[script]\n",
|
|
||||||
);
|
|
||||||
for verilog_file in VerilogJob::all_verilog_files(*main_verilog_file, additional_files)? {
|
|
||||||
output.push("read_verilog -sv -formal \"");
|
|
||||||
output.push(verilog_file);
|
|
||||||
output.push("\"\n");
|
|
||||||
}
|
|
||||||
let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id);
|
|
||||||
// workaround for wires disappearing -- set `keep` on all wires
|
|
||||||
writeln!(
|
|
||||||
output,
|
|
||||||
"hierarchy -top {circuit_name}\n\
|
|
||||||
proc\n\
|
|
||||||
setattr -set keep 1 w:\\*\n\
|
|
||||||
prep",
|
|
||||||
)
|
|
||||||
.expect("writing to OsString can't fail");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobKind for WriteSbyFileJobKind {
|
|
||||||
type Args = FormalArgs;
|
|
||||||
type Job = WriteSbyFileJob;
|
|
||||||
type Dependencies = JobKindAndDependencies<VerilogJobKind>;
|
|
||||||
|
|
||||||
fn dependencies(self) -> Self::Dependencies {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn args_to_jobs(
|
|
||||||
mut args: JobArgsAndDependencies<Self>,
|
|
||||||
params: &JobParams,
|
|
||||||
global_params: &GlobalParams,
|
|
||||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
|
||||||
args.dependencies
|
|
||||||
.dependencies
|
|
||||||
.args
|
|
||||||
.args
|
|
||||||
.additional_args
|
|
||||||
.verilog_dialect
|
|
||||||
.get_or_insert(VerilogDialect::Yosys);
|
|
||||||
args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| {
|
|
||||||
let FormalArgs {
|
|
||||||
sby_extra_args,
|
|
||||||
formal_mode,
|
|
||||||
formal_depth,
|
|
||||||
formal_solver,
|
|
||||||
smtbmc_extra_args,
|
|
||||||
} = args;
|
|
||||||
let base_job = dependencies.get_job::<BaseJob, _>();
|
|
||||||
Ok(WriteSbyFileJob {
|
|
||||||
sby_extra_args: sby_extra_args.into_iter().map(Interned::from).collect(),
|
|
||||||
formal_mode,
|
|
||||||
formal_depth,
|
|
||||||
formal_solver: formal_solver.intern_deref(),
|
|
||||||
smtbmc_extra_args: smtbmc_extra_args.into_iter().map(Interned::from).collect(),
|
|
||||||
sby_file: base_job.file_with_ext("sby"),
|
|
||||||
output_dir: base_job.output_dir(),
|
|
||||||
main_verilog_file: dependencies.get_job::<VerilogJob, _>().main_verilog_file(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> {
|
|
||||||
[JobItemName::DynamicPaths {
|
|
||||||
source_job_name: VerilogJobKind.name(),
|
|
||||||
}]
|
|
||||||
.intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
|
|
||||||
[JobItemName::Path { path: job.sby_file }].intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(self) -> Interned<str> {
|
|
||||||
"write-sby-file".intern()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
self,
|
|
||||||
job: &Self::Job,
|
|
||||||
inputs: &[JobItem],
|
|
||||||
params: &JobParams,
|
|
||||||
_global_params: &GlobalParams,
|
|
||||||
_acquired_job: &mut AcquiredJob,
|
|
||||||
) -> eyre::Result<Vec<JobItem>> {
|
|
||||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
|
||||||
let [additional_files] = inputs else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
let additional_files = VerilogJob::unwrap_additional_files(additional_files);
|
|
||||||
let mut contents = OsString::new();
|
|
||||||
job.write_sby(
|
|
||||||
&mut contents,
|
|
||||||
additional_files,
|
|
||||||
params.main_module().name_id(),
|
|
||||||
)?;
|
|
||||||
let path = job.sby_file;
|
|
||||||
std::fs::write(path, contents.as_encoded_bytes())
|
|
||||||
.wrap_err_with(|| format!("writing {path:?} failed"))?;
|
|
||||||
Ok(vec![JobItem::Path { path }])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subcommand_hidden(self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub struct Formal {
|
|
||||||
#[serde(flatten)]
|
|
||||||
write_sby_file: WriteSbyFileJob,
|
|
||||||
sby_file_name: Interned<OsStr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Formal {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let Self {
|
|
||||||
write_sby_file:
|
|
||||||
WriteSbyFileJob {
|
|
||||||
sby_extra_args,
|
|
||||||
formal_mode,
|
|
||||||
formal_depth,
|
|
||||||
formal_solver,
|
|
||||||
smtbmc_extra_args,
|
|
||||||
sby_file,
|
|
||||||
output_dir: _,
|
|
||||||
main_verilog_file,
|
|
||||||
},
|
|
||||||
sby_file_name,
|
|
||||||
} = self;
|
|
||||||
f.debug_struct("Formal")
|
|
||||||
.field("sby_extra_args", sby_extra_args)
|
|
||||||
.field("formal_mode", formal_mode)
|
|
||||||
.field("formal_depth", formal_depth)
|
|
||||||
.field("formal_solver", formal_solver)
|
|
||||||
.field("smtbmc_extra_args", smtbmc_extra_args)
|
|
||||||
.field("sby_file", sby_file)
|
|
||||||
.field("sby_file_name", sby_file_name)
|
|
||||||
.field("main_verilog_file", main_verilog_file)
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
|
||||||
pub struct Symbiyosys;
|
|
||||||
|
|
||||||
impl ExternalProgramTrait for Symbiyosys {
|
|
||||||
fn default_program_name() -> Interned<str> {
|
|
||||||
"sby".intern()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Hash, PartialEq, Eq, Debug, Args)]
|
|
||||||
pub struct FormalAdditionalArgs {}
|
|
||||||
|
|
||||||
impl ToArgs for FormalAdditionalArgs {
|
|
||||||
fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) {
|
|
||||||
let Self {} = self;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExternalCommand for Formal {
|
|
||||||
type AdditionalArgs = FormalAdditionalArgs;
|
|
||||||
type AdditionalJobData = Formal;
|
|
||||||
type BaseJobPosition = GetJobPositionDependencies<
|
|
||||||
GetJobPositionDependencies<
|
|
||||||
GetJobPositionDependencies<<UnadjustedVerilog as ExternalCommand>::BaseJobPosition>,
|
|
||||||
>,
|
|
||||||
>;
|
|
||||||
type Dependencies = JobKindAndDependencies<WriteSbyFileJobKind>;
|
|
||||||
type ExternalProgram = Symbiyosys;
|
|
||||||
|
|
||||||
fn dependencies() -> Self::Dependencies {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn args_to_jobs(
|
|
||||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
|
||||||
params: &JobParams,
|
|
||||||
global_params: &GlobalParams,
|
|
||||||
) -> eyre::Result<(
|
|
||||||
Self::AdditionalJobData,
|
|
||||||
<Self::Dependencies as JobDependencies>::JobsAndKinds,
|
|
||||||
)> {
|
|
||||||
args.args_to_jobs_external_simple(params, global_params, |args, dependencies| {
|
|
||||||
let FormalAdditionalArgs {} = args.additional_args;
|
|
||||||
let write_sby_file = dependencies.get_job::<WriteSbyFileJob, _>().clone();
|
|
||||||
Ok(Formal {
|
|
||||||
sby_file_name: write_sby_file
|
|
||||||
.sby_file()
|
|
||||||
.interned_file_name()
|
|
||||||
.expect("known to have file name"),
|
|
||||||
write_sby_file,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> {
|
|
||||||
[
|
|
||||||
JobItemName::Path {
|
|
||||||
path: job.additional_job_data().write_sby_file.sby_file(),
|
|
||||||
},
|
|
||||||
JobItemName::Path {
|
|
||||||
path: job.additional_job_data().write_sby_file.main_verilog_file(),
|
|
||||||
},
|
|
||||||
JobItemName::DynamicPaths {
|
|
||||||
source_job_name: VerilogJobKind.name(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn output_paths(_job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
|
|
||||||
Interned::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
|
|
||||||
// args.write_str_arg("-j1"); // sby seems not to respect job count in parallel mode
|
|
||||||
args.write_arg("-f");
|
|
||||||
args.write_interned_arg(job.additional_job_data().sby_file_name);
|
|
||||||
args.write_interned_args(job.additional_job_data().write_sby_file.sby_extra_args());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
|
|
||||||
Some(job.output_dir())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn job_kind_name() -> Interned<str> {
|
|
||||||
"formal".intern()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
|
|
||||||
[
|
|
||||||
DynJobKind::new(WriteSbyFileJobKind),
|
|
||||||
DynJobKind::new(ExternalCommandJobKind::<Formal>::new()),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,855 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
build::{
|
|
||||||
DynJob, GlobalParams, JobItem, JobItemName, JobParams, program_name_for_internal_jobs,
|
|
||||||
},
|
|
||||||
intern::Interned,
|
|
||||||
platform::DynPlatform,
|
|
||||||
util::{HashMap, HashSet, job_server::AcquiredJob},
|
|
||||||
};
|
|
||||||
use eyre::{ContextCompat, eyre};
|
|
||||||
use petgraph::{
|
|
||||||
algo::{DfsSpace, kosaraju_scc, toposort},
|
|
||||||
graph::{DiGraph, NodeIndex},
|
|
||||||
visit::{GraphBase, Visitable},
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::SerializeSeq};
|
|
||||||
use std::{
|
|
||||||
cell::OnceCell,
|
|
||||||
collections::{BTreeMap, BTreeSet, VecDeque},
|
|
||||||
convert::Infallible,
|
|
||||||
ffi::OsStr,
|
|
||||||
fmt::{self, Write},
|
|
||||||
panic,
|
|
||||||
rc::Rc,
|
|
||||||
str::Utf8Error,
|
|
||||||
sync::mpsc,
|
|
||||||
thread::{self, ScopedJoinHandle},
|
|
||||||
};
|
|
||||||
|
|
||||||
macro_rules! write_str {
|
|
||||||
($s:expr, $($rest:tt)*) => {
|
|
||||||
write!($s, $($rest)*).expect("String::write_fmt can't fail")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
enum JobGraphNode {
|
|
||||||
Job(DynJob),
|
|
||||||
Item {
|
|
||||||
#[allow(dead_code, reason = "name used for debugging")]
|
|
||||||
name: JobItemName,
|
|
||||||
source_job: Option<DynJob>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type JobGraphInner = DiGraph<JobGraphNode, ()>;
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
pub struct JobGraph {
|
|
||||||
jobs: HashMap<DynJob, <JobGraphInner as GraphBase>::NodeId>,
|
|
||||||
items: HashMap<JobItemName, <JobGraphInner as GraphBase>::NodeId>,
|
|
||||||
graph: JobGraphInner,
|
|
||||||
topological_order: Vec<<JobGraphInner as GraphBase>::NodeId>,
|
|
||||||
space: DfsSpace<<JobGraphInner as GraphBase>::NodeId, <JobGraphInner as Visitable>::Map>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for JobGraph {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let Self {
|
|
||||||
jobs: _,
|
|
||||||
items: _,
|
|
||||||
graph,
|
|
||||||
topological_order,
|
|
||||||
space: _,
|
|
||||||
} = self;
|
|
||||||
f.debug_struct("JobGraph")
|
|
||||||
.field("graph", graph)
|
|
||||||
.field("topological_order", topological_order)
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum JobGraphError {
|
|
||||||
CycleError {
|
|
||||||
job: DynJob,
|
|
||||||
output: JobItemName,
|
|
||||||
},
|
|
||||||
MultipleJobsCreateSameOutput {
|
|
||||||
output_item: JobItemName,
|
|
||||||
existing_job: DynJob,
|
|
||||||
new_job: DynJob,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for JobGraphError {}
|
|
||||||
|
|
||||||
impl fmt::Display for JobGraphError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::CycleError { job, output } => write!(
|
|
||||||
f,
|
|
||||||
"job can't be added to job graph because it would introduce a cyclic dependency through this job output:\n\
|
|
||||||
{output:?}\n\
|
|
||||||
job:\n{job:?}",
|
|
||||||
),
|
|
||||||
JobGraphError::MultipleJobsCreateSameOutput {
|
|
||||||
output_item,
|
|
||||||
existing_job,
|
|
||||||
new_job,
|
|
||||||
} => write!(
|
|
||||||
f,
|
|
||||||
"job can't be added to job graph because the new job has an output that is also produced by an existing job.\n\
|
|
||||||
conflicting output:\n\
|
|
||||||
{output_item:?}\n\
|
|
||||||
existing job:\n\
|
|
||||||
{existing_job:?}\n\
|
|
||||||
new job:\n\
|
|
||||||
{new_job:?}",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
enum EscapeForUnixShellState {
|
|
||||||
DollarSingleQuote,
|
|
||||||
SingleQuote,
|
|
||||||
Unquoted,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct EscapeForUnixShell<'a> {
|
|
||||||
state: EscapeForUnixShellState,
|
|
||||||
prefix: [u8; 3],
|
|
||||||
bytes: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> fmt::Debug for EscapeForUnixShell<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt::Display::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> fmt::Display for EscapeForUnixShell<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
for c in self.clone() {
|
|
||||||
f.write_char(c)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> EscapeForUnixShell<'a> {
|
|
||||||
pub fn new(s: &'a (impl ?Sized + AsRef<OsStr>)) -> Self {
|
|
||||||
Self::from_bytes(s.as_ref().as_encoded_bytes())
|
|
||||||
}
|
|
||||||
fn make_prefix(bytes: &[u8]) -> [u8; 3] {
|
|
||||||
let mut prefix = [0; 3];
|
|
||||||
prefix[..bytes.len()].copy_from_slice(bytes);
|
|
||||||
prefix
|
|
||||||
}
|
|
||||||
pub fn from_bytes(bytes: &'a [u8]) -> Self {
|
|
||||||
let mut needs_single_quote = bytes.is_empty();
|
|
||||||
for &b in bytes {
|
|
||||||
match b {
|
|
||||||
b'!' | b'\'' | b'\"' | b' ' => needs_single_quote = true,
|
|
||||||
0..0x20 | 0x7F.. => {
|
|
||||||
return Self {
|
|
||||||
state: EscapeForUnixShellState::DollarSingleQuote,
|
|
||||||
prefix: Self::make_prefix(b"$'"),
|
|
||||||
bytes,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if needs_single_quote {
|
|
||||||
Self {
|
|
||||||
state: EscapeForUnixShellState::SingleQuote,
|
|
||||||
prefix: Self::make_prefix(b"'"),
|
|
||||||
bytes,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Self {
|
|
||||||
state: EscapeForUnixShellState::Unquoted,
|
|
||||||
prefix: Self::make_prefix(b""),
|
|
||||||
bytes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for EscapeForUnixShell<'_> {
|
|
||||||
type Item = char;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
match &mut self.prefix {
|
|
||||||
[0, 0, 0] => {}
|
|
||||||
[0, 0, v] | // find first
|
|
||||||
[0, v, _] | // non-zero byte
|
|
||||||
[v, _, _] => {
|
|
||||||
let retval = *v as char;
|
|
||||||
*v = 0;
|
|
||||||
return Some(retval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let Some(&next_byte) = self.bytes.split_off_first() else {
|
|
||||||
return match self.state {
|
|
||||||
EscapeForUnixShellState::DollarSingleQuote
|
|
||||||
| EscapeForUnixShellState::SingleQuote => {
|
|
||||||
self.state = EscapeForUnixShellState::Unquoted;
|
|
||||||
Some('\'')
|
|
||||||
}
|
|
||||||
EscapeForUnixShellState::Unquoted => None,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
match self.state {
|
|
||||||
EscapeForUnixShellState::DollarSingleQuote => match next_byte {
|
|
||||||
b'\'' | b'\\' => {
|
|
||||||
self.prefix = Self::make_prefix(&[next_byte]);
|
|
||||||
Some('\\')
|
|
||||||
}
|
|
||||||
b'\t' => {
|
|
||||||
self.prefix = Self::make_prefix(b"t");
|
|
||||||
Some('\\')
|
|
||||||
}
|
|
||||||
b'\n' => {
|
|
||||||
self.prefix = Self::make_prefix(b"n");
|
|
||||||
Some('\\')
|
|
||||||
}
|
|
||||||
b'\r' => {
|
|
||||||
self.prefix = Self::make_prefix(b"r");
|
|
||||||
Some('\\')
|
|
||||||
}
|
|
||||||
0x20..=0x7E => Some(next_byte as char),
|
|
||||||
_ => {
|
|
||||||
self.prefix = [
|
|
||||||
b'x',
|
|
||||||
char::from_digit(next_byte as u32 >> 4, 0x10).expect("known to be in range")
|
|
||||||
as u8,
|
|
||||||
char::from_digit(next_byte as u32 & 0xF, 0x10)
|
|
||||||
.expect("known to be in range") as u8,
|
|
||||||
];
|
|
||||||
Some('\\')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
EscapeForUnixShellState::SingleQuote => {
|
|
||||||
if next_byte == b'\'' {
|
|
||||||
self.prefix = Self::make_prefix(b"\\''");
|
|
||||||
Some('\'')
|
|
||||||
} else {
|
|
||||||
Some(next_byte as char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EscapeForUnixShellState::Unquoted => match next_byte {
|
|
||||||
b' ' | b'!' | b'"' | b'#' | b'$' | b'&' | b'\'' | b'(' | b')' | b'*' | b','
|
|
||||||
| b';' | b'<' | b'>' | b'?' | b'[' | b'\\' | b']' | b'^' | b'`' | b'{' | b'|'
|
|
||||||
| b'}' | b'~' => {
|
|
||||||
self.prefix = Self::make_prefix(&[next_byte]);
|
|
||||||
Some('\\')
|
|
||||||
}
|
|
||||||
_ => Some(next_byte as char),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum UnixMakefileEscapeKind {
|
|
||||||
NonRecipe,
|
|
||||||
RecipeWithoutShellEscaping,
|
|
||||||
RecipeWithShellEscaping,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct EscapeForUnixMakefile<'a> {
|
|
||||||
s: &'a OsStr,
|
|
||||||
kind: UnixMakefileEscapeKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> fmt::Debug for EscapeForUnixMakefile<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt::Display::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> fmt::Display for EscapeForUnixMakefile<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
self.do_write(
|
|
||||||
f,
|
|
||||||
fmt::Write::write_str,
|
|
||||||
fmt::Write::write_char,
|
|
||||||
|_, _| Ok(()),
|
|
||||||
|_| unreachable!("already checked that the input causes no UTF-8 errors"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> EscapeForUnixMakefile<'a> {
|
|
||||||
fn do_write<S: ?Sized, E>(
|
|
||||||
&self,
|
|
||||||
state: &mut S,
|
|
||||||
write_str: impl Fn(&mut S, &str) -> Result<(), E>,
|
|
||||||
write_char: impl Fn(&mut S, char) -> Result<(), E>,
|
|
||||||
add_variable: impl Fn(&mut S, &'static str) -> Result<(), E>,
|
|
||||||
utf8_error: impl Fn(Utf8Error) -> E,
|
|
||||||
) -> Result<(), E> {
|
|
||||||
let escape_recipe_char = |c| match c {
|
|
||||||
'$' => write_str(state, "$$"),
|
|
||||||
'\0'..='\x1F' | '\x7F' => {
|
|
||||||
panic!("can't escape a control character for Unix Makefile: {c:?}");
|
|
||||||
}
|
|
||||||
_ => write_char(state, c),
|
|
||||||
};
|
|
||||||
match self.kind {
|
|
||||||
UnixMakefileEscapeKind::NonRecipe => str::from_utf8(self.s.as_encoded_bytes())
|
|
||||||
.map_err(&utf8_error)?
|
|
||||||
.chars()
|
|
||||||
.try_for_each(|c| match c {
|
|
||||||
'=' => {
|
|
||||||
add_variable(state, "EQUALS = =")?;
|
|
||||||
write_str(state, "$(EQUALS)")
|
|
||||||
}
|
|
||||||
';' => panic!("can't escape a semicolon (;) for Unix Makefile"),
|
|
||||||
'$' => write_str(state, "$$"),
|
|
||||||
'\\' | ' ' | '#' | ':' | '%' | '*' | '?' | '[' | ']' | '~' => {
|
|
||||||
write_char(state, '\\')?;
|
|
||||||
write_char(state, c)
|
|
||||||
}
|
|
||||||
'\0'..='\x1F' | '\x7F' => {
|
|
||||||
panic!("can't escape a control character for Unix Makefile: {c:?}");
|
|
||||||
}
|
|
||||||
_ => write_char(state, c),
|
|
||||||
}),
|
|
||||||
UnixMakefileEscapeKind::RecipeWithoutShellEscaping => {
|
|
||||||
str::from_utf8(self.s.as_encoded_bytes())
|
|
||||||
.map_err(&utf8_error)?
|
|
||||||
.chars()
|
|
||||||
.try_for_each(escape_recipe_char)
|
|
||||||
}
|
|
||||||
UnixMakefileEscapeKind::RecipeWithShellEscaping => {
|
|
||||||
EscapeForUnixShell::new(self.s).try_for_each(escape_recipe_char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn new(
|
|
||||||
s: &'a (impl ?Sized + AsRef<OsStr>),
|
|
||||||
kind: UnixMakefileEscapeKind,
|
|
||||||
needed_variables: &mut BTreeSet<&'static str>,
|
|
||||||
) -> Result<Self, Utf8Error> {
|
|
||||||
let s = s.as_ref();
|
|
||||||
let retval = Self { s, kind };
|
|
||||||
retval.do_write(
|
|
||||||
needed_variables,
|
|
||||||
|_, _| Ok(()),
|
|
||||||
|_, _| Ok(()),
|
|
||||||
|needed_variables, variable| {
|
|
||||||
needed_variables.insert(variable);
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
|e| e,
|
|
||||||
)?;
|
|
||||||
Ok(retval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobGraph {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
fn try_add_item_node(
|
|
||||||
&mut self,
|
|
||||||
name: JobItemName,
|
|
||||||
new_source_job: Option<DynJob>,
|
|
||||||
new_nodes: &mut HashSet<<JobGraphInner as GraphBase>::NodeId>,
|
|
||||||
) -> Result<<JobGraphInner as GraphBase>::NodeId, JobGraphError> {
|
|
||||||
use hashbrown::hash_map::Entry;
|
|
||||||
match self.items.entry(name) {
|
|
||||||
Entry::Occupied(item_entry) => {
|
|
||||||
let node_id = *item_entry.get();
|
|
||||||
let JobGraphNode::Item {
|
|
||||||
name: _,
|
|
||||||
source_job,
|
|
||||||
} = &mut self.graph[node_id]
|
|
||||||
else {
|
|
||||||
unreachable!("known to be an item");
|
|
||||||
};
|
|
||||||
if let Some(new_source_job) = new_source_job {
|
|
||||||
if let Some(source_job) = source_job {
|
|
||||||
return Err(JobGraphError::MultipleJobsCreateSameOutput {
|
|
||||||
output_item: item_entry.key().clone(),
|
|
||||||
existing_job: source_job.clone(),
|
|
||||||
new_job: new_source_job,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
*source_job = Some(new_source_job);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(node_id)
|
|
||||||
}
|
|
||||||
Entry::Vacant(item_entry) => {
|
|
||||||
let node_id = self.graph.add_node(JobGraphNode::Item {
|
|
||||||
name,
|
|
||||||
source_job: new_source_job,
|
|
||||||
});
|
|
||||||
new_nodes.insert(node_id);
|
|
||||||
item_entry.insert(node_id);
|
|
||||||
Ok(node_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn try_add_jobs<I: IntoIterator<Item = DynJob>>(
|
|
||||||
&mut self,
|
|
||||||
jobs: I,
|
|
||||||
) -> Result<(), JobGraphError> {
|
|
||||||
use hashbrown::hash_map::Entry;
|
|
||||||
let jobs = jobs.into_iter();
|
|
||||||
struct RemoveNewNodesOnError<'a> {
|
|
||||||
this: &'a mut JobGraph,
|
|
||||||
new_nodes: HashSet<<JobGraphInner as GraphBase>::NodeId>,
|
|
||||||
}
|
|
||||||
impl Drop for RemoveNewNodesOnError<'_> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
for node in self.new_nodes.drain() {
|
|
||||||
self.this.graph.remove_node(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut remove_new_nodes_on_error = RemoveNewNodesOnError {
|
|
||||||
this: self,
|
|
||||||
new_nodes: HashSet::with_capacity_and_hasher(jobs.size_hint().0, Default::default()),
|
|
||||||
};
|
|
||||||
let new_nodes = &mut remove_new_nodes_on_error.new_nodes;
|
|
||||||
let this = &mut *remove_new_nodes_on_error.this;
|
|
||||||
for job in jobs {
|
|
||||||
let Entry::Vacant(job_entry) = this.jobs.entry(job.clone()) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let job_node_id = this
|
|
||||||
.graph
|
|
||||||
.add_node(JobGraphNode::Job(job_entry.key().clone()));
|
|
||||||
new_nodes.insert(job_node_id);
|
|
||||||
job_entry.insert(job_node_id);
|
|
||||||
for name in job.outputs() {
|
|
||||||
let item_node_id = this.try_add_item_node(name, Some(job.clone()), new_nodes)?;
|
|
||||||
this.graph.add_edge(job_node_id, item_node_id, ());
|
|
||||||
}
|
|
||||||
for name in job.inputs() {
|
|
||||||
let item_node_id = this.try_add_item_node(name, None, new_nodes)?;
|
|
||||||
this.graph.add_edge(item_node_id, job_node_id, ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match toposort(&this.graph, Some(&mut this.space)) {
|
|
||||||
Ok(v) => {
|
|
||||||
this.topological_order = v;
|
|
||||||
// no need to remove any of the new nodes on drop since we didn't encounter any errors
|
|
||||||
remove_new_nodes_on_error.new_nodes.clear();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// there's at least one cycle, find one!
|
|
||||||
let cycle = kosaraju_scc(&this.graph)
|
|
||||||
.into_iter()
|
|
||||||
.find_map(|scc| {
|
|
||||||
if scc.len() <= 1 {
|
|
||||||
// can't be a cycle since our graph is bipartite --
|
|
||||||
// jobs only connect to items, never jobs to jobs or items to items
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(scc)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.expect("we know there's a cycle");
|
|
||||||
let cycle_set = HashSet::<NodeIndex>::from_iter(cycle.iter().copied());
|
|
||||||
let job = cycle
|
|
||||||
.into_iter()
|
|
||||||
.find_map(|node_id| {
|
|
||||||
if let JobGraphNode::Job(job) = &this.graph[node_id] {
|
|
||||||
Some(job.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.expect("a job must be part of the cycle");
|
|
||||||
let output = job
|
|
||||||
.outputs()
|
|
||||||
.into_iter()
|
|
||||||
.find(|output| cycle_set.contains(&this.items[output]))
|
|
||||||
.expect("an output must be part of the cycle");
|
|
||||||
Err(JobGraphError::CycleError { job, output })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn add_jobs<I: IntoIterator<Item = DynJob>>(&mut self, jobs: I) {
|
|
||||||
match self.try_add_jobs(jobs) {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(e) => panic!("error: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn to_unix_makefile(
|
|
||||||
&self,
|
|
||||||
platform: Option<&DynPlatform>,
|
|
||||||
extra_args: &[Interned<OsStr>],
|
|
||||||
) -> Result<String, Utf8Error> {
|
|
||||||
self.to_unix_makefile_with_internal_program_prefix(
|
|
||||||
&[program_name_for_internal_jobs()],
|
|
||||||
platform,
|
|
||||||
extra_args,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn to_unix_makefile_with_internal_program_prefix(
|
|
||||||
&self,
|
|
||||||
internal_program_prefix: &[Interned<OsStr>],
|
|
||||||
platform: Option<&DynPlatform>,
|
|
||||||
extra_args: &[Interned<OsStr>],
|
|
||||||
) -> Result<String, Utf8Error> {
|
|
||||||
let mut retval = String::new();
|
|
||||||
let mut needed_variables = BTreeSet::new();
|
|
||||||
let mut phony_targets = BTreeSet::new();
|
|
||||||
for &node_id in &self.topological_order {
|
|
||||||
let JobGraphNode::Job(job) = &self.graph[node_id] else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let outputs = job.outputs();
|
|
||||||
if outputs.is_empty() {
|
|
||||||
retval.push_str(":");
|
|
||||||
} else {
|
|
||||||
for output in job.outputs() {
|
|
||||||
match output {
|
|
||||||
JobItemName::Path { path } => {
|
|
||||||
write_str!(
|
|
||||||
retval,
|
|
||||||
"{} ",
|
|
||||||
EscapeForUnixMakefile::new(
|
|
||||||
&str::from_utf8(path.as_os_str().as_encoded_bytes())?,
|
|
||||||
UnixMakefileEscapeKind::NonRecipe,
|
|
||||||
&mut needed_variables
|
|
||||||
)?
|
|
||||||
);
|
|
||||||
}
|
|
||||||
JobItemName::DynamicPaths { source_job_name } => {
|
|
||||||
write_str!(
|
|
||||||
retval,
|
|
||||||
"{} ",
|
|
||||||
EscapeForUnixMakefile::new(
|
|
||||||
&source_job_name,
|
|
||||||
UnixMakefileEscapeKind::NonRecipe,
|
|
||||||
&mut needed_variables
|
|
||||||
)?
|
|
||||||
);
|
|
||||||
phony_targets.insert(Interned::into_inner(source_job_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if outputs.len() == 1 {
|
|
||||||
retval.push_str(":");
|
|
||||||
} else {
|
|
||||||
retval.push_str("&:");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for input in job.inputs() {
|
|
||||||
match input {
|
|
||||||
JobItemName::Path { path } => {
|
|
||||||
write_str!(
|
|
||||||
retval,
|
|
||||||
" {}",
|
|
||||||
EscapeForUnixMakefile::new(
|
|
||||||
&str::from_utf8(path.as_os_str().as_encoded_bytes())?,
|
|
||||||
UnixMakefileEscapeKind::NonRecipe,
|
|
||||||
&mut needed_variables
|
|
||||||
)?
|
|
||||||
);
|
|
||||||
}
|
|
||||||
JobItemName::DynamicPaths { source_job_name } => {
|
|
||||||
write_str!(
|
|
||||||
retval,
|
|
||||||
" {}",
|
|
||||||
EscapeForUnixMakefile::new(
|
|
||||||
&source_job_name,
|
|
||||||
UnixMakefileEscapeKind::NonRecipe,
|
|
||||||
&mut needed_variables
|
|
||||||
)?
|
|
||||||
);
|
|
||||||
phony_targets.insert(Interned::into_inner(source_job_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
retval.push_str("\n\t");
|
|
||||||
job.command_params_with_internal_program_prefix(
|
|
||||||
internal_program_prefix,
|
|
||||||
platform,
|
|
||||||
extra_args,
|
|
||||||
)
|
|
||||||
.to_unix_shell_line(&mut retval, |arg, output| {
|
|
||||||
write_str!(
|
|
||||||
output,
|
|
||||||
"{}",
|
|
||||||
EscapeForUnixMakefile::new(
|
|
||||||
arg,
|
|
||||||
UnixMakefileEscapeKind::RecipeWithShellEscaping,
|
|
||||||
&mut needed_variables
|
|
||||||
)?
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
retval.push_str("\n\n");
|
|
||||||
}
|
|
||||||
if !phony_targets.is_empty() {
|
|
||||||
retval.push_str("\n.PHONY:");
|
|
||||||
for phony_target in phony_targets {
|
|
||||||
write_str!(
|
|
||||||
retval,
|
|
||||||
" {}",
|
|
||||||
EscapeForUnixMakefile::new(
|
|
||||||
phony_target,
|
|
||||||
UnixMakefileEscapeKind::NonRecipe,
|
|
||||||
&mut needed_variables
|
|
||||||
)?
|
|
||||||
);
|
|
||||||
}
|
|
||||||
retval.push_str("\n");
|
|
||||||
}
|
|
||||||
if !needed_variables.is_empty() {
|
|
||||||
retval.insert_str(
|
|
||||||
0,
|
|
||||||
&String::from_iter(needed_variables.into_iter().map(|v| format!("{v}\n"))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(retval)
|
|
||||||
}
|
|
||||||
pub fn to_unix_shell_script(
|
|
||||||
&self,
|
|
||||||
platform: Option<&DynPlatform>,
|
|
||||||
extra_args: &[Interned<OsStr>],
|
|
||||||
) -> String {
|
|
||||||
self.to_unix_shell_script_with_internal_program_prefix(
|
|
||||||
&[program_name_for_internal_jobs()],
|
|
||||||
platform,
|
|
||||||
extra_args,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn to_unix_shell_script_with_internal_program_prefix(
|
|
||||||
&self,
|
|
||||||
internal_program_prefix: &[Interned<OsStr>],
|
|
||||||
platform: Option<&DynPlatform>,
|
|
||||||
extra_args: &[Interned<OsStr>],
|
|
||||||
) -> String {
|
|
||||||
let mut retval = String::from(
|
|
||||||
"#!/bin/sh\n\
|
|
||||||
set -ex\n",
|
|
||||||
);
|
|
||||||
for &node_id in &self.topological_order {
|
|
||||||
let JobGraphNode::Job(job) = &self.graph[node_id] else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let Ok(()) = job
|
|
||||||
.command_params_with_internal_program_prefix(
|
|
||||||
internal_program_prefix,
|
|
||||||
platform,
|
|
||||||
extra_args,
|
|
||||||
)
|
|
||||||
.to_unix_shell_line(&mut retval, |arg, output| -> Result<(), Infallible> {
|
|
||||||
write_str!(output, "{}", EscapeForUnixShell::new(&arg));
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
retval.push_str("\n");
|
|
||||||
}
|
|
||||||
retval
|
|
||||||
}
|
|
||||||
pub fn run(&self, params: &JobParams, global_params: &GlobalParams) -> eyre::Result<()> {
|
|
||||||
// use scope to auto-join threads on errors
|
|
||||||
thread::scope(|scope| {
|
|
||||||
struct WaitingJobState {
|
|
||||||
job_node_id: <JobGraphInner as GraphBase>::NodeId,
|
|
||||||
job: DynJob,
|
|
||||||
inputs: BTreeMap<JobItemName, OnceCell<JobItem>>,
|
|
||||||
}
|
|
||||||
let mut ready_jobs = VecDeque::new();
|
|
||||||
let mut item_name_to_waiting_jobs_map = HashMap::<_, Vec<_>>::default();
|
|
||||||
for &node_id in &self.topological_order {
|
|
||||||
let JobGraphNode::Job(job) = &self.graph[node_id] else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let waiting_job = WaitingJobState {
|
|
||||||
job_node_id: node_id,
|
|
||||||
job: job.clone(),
|
|
||||||
inputs: job
|
|
||||||
.inputs()
|
|
||||||
.iter()
|
|
||||||
.map(|&name| (name, OnceCell::new()))
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
if waiting_job.inputs.is_empty() {
|
|
||||||
ready_jobs.push_back(waiting_job);
|
|
||||||
} else {
|
|
||||||
let waiting_job = Rc::new(waiting_job);
|
|
||||||
for &input_item in waiting_job.inputs.keys() {
|
|
||||||
item_name_to_waiting_jobs_map
|
|
||||||
.entry(input_item)
|
|
||||||
.or_default()
|
|
||||||
.push(waiting_job.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
struct RunningJob<'scope> {
|
|
||||||
job: DynJob,
|
|
||||||
thread: ScopedJoinHandle<'scope, eyre::Result<Vec<JobItem>>>,
|
|
||||||
}
|
|
||||||
let mut running_jobs = HashMap::<NodeIndex, RunningJob>::default();
|
|
||||||
let (finished_jobs_sender, finished_jobs_receiver) = mpsc::channel();
|
|
||||||
let mut next_finished_job = None;
|
|
||||||
loop {
|
|
||||||
if let Some(finished_job) = next_finished_job
|
|
||||||
.take()
|
|
||||||
.or_else(|| finished_jobs_receiver.try_recv().ok())
|
|
||||||
{
|
|
||||||
let Some(RunningJob { job, thread }) = running_jobs.remove(&finished_job)
|
|
||||||
else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
let output_items = thread.join().map_err(panic::resume_unwind)??;
|
|
||||||
assert!(
|
|
||||||
output_items.iter().map(JobItem::name).eq(job.outputs()),
|
|
||||||
"job's run() method returned the wrong output items:\n\
|
|
||||||
output items:\n\
|
|
||||||
{output_items:?}\n\
|
|
||||||
expected outputs:\n\
|
|
||||||
{:?}\n\
|
|
||||||
job:\n\
|
|
||||||
{job:?}",
|
|
||||||
job.outputs(),
|
|
||||||
);
|
|
||||||
for output_item in output_items {
|
|
||||||
for waiting_job in item_name_to_waiting_jobs_map
|
|
||||||
.remove(&output_item.name())
|
|
||||||
.unwrap_or_default()
|
|
||||||
{
|
|
||||||
let Ok(()) =
|
|
||||||
waiting_job.inputs[&output_item.name()].set(output_item.clone())
|
|
||||||
else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
if let Some(waiting_job) = Rc::into_inner(waiting_job) {
|
|
||||||
ready_jobs.push_back(waiting_job);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some(WaitingJobState {
|
|
||||||
job_node_id,
|
|
||||||
job,
|
|
||||||
inputs,
|
|
||||||
}) = ready_jobs.pop_front()
|
|
||||||
{
|
|
||||||
struct RunningJobInThread<'a> {
|
|
||||||
job_node_id: <JobGraphInner as GraphBase>::NodeId,
|
|
||||||
job: DynJob,
|
|
||||||
inputs: Vec<JobItem>,
|
|
||||||
params: &'a JobParams,
|
|
||||||
global_params: &'a GlobalParams,
|
|
||||||
acquired_job: AcquiredJob,
|
|
||||||
finished_jobs_sender: mpsc::Sender<<JobGraphInner as GraphBase>::NodeId>,
|
|
||||||
}
|
|
||||||
impl RunningJobInThread<'_> {
|
|
||||||
fn run(mut self) -> eyre::Result<Vec<JobItem>> {
|
|
||||||
self.job.run(
|
|
||||||
&self.inputs,
|
|
||||||
self.params,
|
|
||||||
self.global_params,
|
|
||||||
&mut self.acquired_job,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for RunningJobInThread<'_> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = self.finished_jobs_sender.send(self.job_node_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let name = job.kind().name();
|
|
||||||
let running_job_in_thread = RunningJobInThread {
|
|
||||||
job_node_id,
|
|
||||||
job: job.clone(),
|
|
||||||
inputs: Result::from_iter(job.inputs().iter().map(|input_name| {
|
|
||||||
inputs.get(input_name).and_then(|v| v.get().cloned()).wrap_err_with(|| {
|
|
||||||
eyre!("failed when trying to run job {name}: nothing provided the input item: {input_name:?}")
|
|
||||||
})
|
|
||||||
}))?,
|
|
||||||
params,
|
|
||||||
global_params,
|
|
||||||
acquired_job: AcquiredJob::acquire()?,
|
|
||||||
finished_jobs_sender: finished_jobs_sender.clone(),
|
|
||||||
};
|
|
||||||
running_jobs.insert(
|
|
||||||
job_node_id,
|
|
||||||
RunningJob {
|
|
||||||
job,
|
|
||||||
thread: thread::Builder::new()
|
|
||||||
.name(format!("job:{name}"))
|
|
||||||
.spawn_scoped(scope, move || running_job_in_thread.run())
|
|
||||||
.expect("failed to spawn thread for job"),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if running_jobs.is_empty() {
|
|
||||||
assert!(item_name_to_waiting_jobs_map.is_empty());
|
|
||||||
assert!(ready_jobs.is_empty());
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
// nothing to do yet, block to avoid busy waiting
|
|
||||||
next_finished_job = finished_jobs_receiver.recv().ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Extend<DynJob> for JobGraph {
|
|
||||||
#[track_caller]
|
|
||||||
fn extend<T: IntoIterator<Item = DynJob>>(&mut self, iter: T) {
|
|
||||||
self.add_jobs(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromIterator<DynJob> for JobGraph {
|
|
||||||
#[track_caller]
|
|
||||||
fn from_iter<T: IntoIterator<Item = DynJob>>(iter: T) -> Self {
|
|
||||||
let mut retval = Self::new();
|
|
||||||
retval.add_jobs(iter);
|
|
||||||
retval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for JobGraph {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let mut serializer = serializer.serialize_seq(Some(self.jobs.len()))?;
|
|
||||||
for &node_id in &self.topological_order {
|
|
||||||
let JobGraphNode::Job(job) = &self.graph[node_id] else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
serializer.serialize_element(job)?;
|
|
||||||
}
|
|
||||||
serializer.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for JobGraph {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let jobs = Vec::<DynJob>::deserialize(deserializer)?;
|
|
||||||
let mut retval = JobGraph::new();
|
|
||||||
retval.try_add_jobs(jobs).map_err(D::Error::custom)?;
|
|
||||||
Ok(retval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,313 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
build::{DynJobKind, JobKind, built_in_job_kinds},
|
|
||||||
intern::Interned,
|
|
||||||
util::InternedStrCompareAsStr,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::BTreeMap,
|
|
||||||
fmt,
|
|
||||||
sync::{Arc, OnceLock, RwLock, RwLockWriteGuard},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl DynJobKind {
|
|
||||||
pub fn registry() -> JobKindRegistrySnapshot {
|
|
||||||
JobKindRegistrySnapshot(JobKindRegistry::get())
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn register(self) {
|
|
||||||
JobKindRegistry::register(JobKindRegistry::lock(), self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct JobKindRegistry {
|
|
||||||
job_kinds: BTreeMap<InternedStrCompareAsStr, DynJobKind>,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum JobKindRegisterError {
|
|
||||||
SameName {
|
|
||||||
name: InternedStrCompareAsStr,
|
|
||||||
old_job_kind: DynJobKind,
|
|
||||||
new_job_kind: DynJobKind,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for JobKindRegisterError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::SameName {
|
|
||||||
name,
|
|
||||||
old_job_kind,
|
|
||||||
new_job_kind,
|
|
||||||
} => write!(
|
|
||||||
f,
|
|
||||||
"two different `JobKind` can't share the same name:\n\
|
|
||||||
{name:?}\n\
|
|
||||||
old job kind:\n\
|
|
||||||
{old_job_kind:?}\n\
|
|
||||||
new job kind:\n\
|
|
||||||
{new_job_kind:?}",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait JobKindRegistryRegisterLock {
|
|
||||||
type Locked;
|
|
||||||
fn lock(self) -> Self::Locked;
|
|
||||||
fn make_mut(locked: &mut Self::Locked) -> &mut JobKindRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobKindRegistryRegisterLock for &'static RwLock<Arc<JobKindRegistry>> {
|
|
||||||
type Locked = RwLockWriteGuard<'static, Arc<JobKindRegistry>>;
|
|
||||||
fn lock(self) -> Self::Locked {
|
|
||||||
self.write().expect("shouldn't be poisoned")
|
|
||||||
}
|
|
||||||
fn make_mut(locked: &mut Self::Locked) -> &mut JobKindRegistry {
|
|
||||||
Arc::make_mut(locked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobKindRegistryRegisterLock for &'_ mut JobKindRegistry {
|
|
||||||
type Locked = Self;
|
|
||||||
|
|
||||||
fn lock(self) -> Self::Locked {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_mut(locked: &mut Self::Locked) -> &mut JobKindRegistry {
|
|
||||||
locked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobKindRegistry {
|
|
||||||
fn lock() -> &'static RwLock<Arc<Self>> {
|
|
||||||
static REGISTRY: OnceLock<RwLock<Arc<JobKindRegistry>>> = OnceLock::new();
|
|
||||||
REGISTRY.get_or_init(Default::default)
|
|
||||||
}
|
|
||||||
fn try_register<L: JobKindRegistryRegisterLock>(
|
|
||||||
lock: L,
|
|
||||||
job_kind: DynJobKind,
|
|
||||||
) -> Result<(), JobKindRegisterError> {
|
|
||||||
use std::collections::btree_map::Entry;
|
|
||||||
let name = InternedStrCompareAsStr(job_kind.name());
|
|
||||||
// run user code only outside of lock
|
|
||||||
let mut locked = lock.lock();
|
|
||||||
let this = L::make_mut(&mut locked);
|
|
||||||
let result = match this.job_kinds.entry(name) {
|
|
||||||
Entry::Occupied(entry) => Err(JobKindRegisterError::SameName {
|
|
||||||
name,
|
|
||||||
old_job_kind: entry.get().clone(),
|
|
||||||
new_job_kind: job_kind,
|
|
||||||
}),
|
|
||||||
Entry::Vacant(entry) => {
|
|
||||||
entry.insert(job_kind);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
drop(locked);
|
|
||||||
// outside of lock now, so we can test if it's the same DynJobKind
|
|
||||||
match result {
|
|
||||||
Err(JobKindRegisterError::SameName {
|
|
||||||
name: _,
|
|
||||||
old_job_kind,
|
|
||||||
new_job_kind,
|
|
||||||
}) if old_job_kind == new_job_kind => Ok(()),
|
|
||||||
result => result,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn register<L: JobKindRegistryRegisterLock>(lock: L, job_kind: DynJobKind) {
|
|
||||||
match Self::try_register(lock, job_kind) {
|
|
||||||
Err(e) => panic!("{e}"),
|
|
||||||
Ok(()) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn get() -> Arc<Self> {
|
|
||||||
Self::lock().read().expect("shouldn't be poisoned").clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for JobKindRegistry {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut retval = Self {
|
|
||||||
job_kinds: BTreeMap::new(),
|
|
||||||
};
|
|
||||||
for job_kind in built_in_job_kinds() {
|
|
||||||
Self::register(&mut retval, job_kind);
|
|
||||||
}
|
|
||||||
retval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct JobKindRegistrySnapshot(Arc<JobKindRegistry>);
|
|
||||||
|
|
||||||
impl JobKindRegistrySnapshot {
|
|
||||||
pub fn get() -> Self {
|
|
||||||
JobKindRegistrySnapshot(JobKindRegistry::get())
|
|
||||||
}
|
|
||||||
pub fn get_by_name<'a>(&'a self, name: &str) -> Option<&'a DynJobKind> {
|
|
||||||
self.0.job_kinds.get(name)
|
|
||||||
}
|
|
||||||
pub fn iter_with_names(&self) -> JobKindRegistryIterWithNames<'_> {
|
|
||||||
JobKindRegistryIterWithNames(self.0.job_kinds.iter())
|
|
||||||
}
|
|
||||||
pub fn iter(&self) -> JobKindRegistryIter<'_> {
|
|
||||||
JobKindRegistryIter(self.0.job_kinds.values())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a JobKindRegistrySnapshot {
|
|
||||||
type Item = &'a DynJobKind;
|
|
||||||
type IntoIter = JobKindRegistryIter<'a>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a mut JobKindRegistrySnapshot {
|
|
||||||
type Item = &'a DynJobKind;
|
|
||||||
type IntoIter = JobKindRegistryIter<'a>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct JobKindRegistryIter<'a>(
|
|
||||||
std::collections::btree_map::Values<'a, InternedStrCompareAsStr, DynJobKind>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<'a> Iterator for JobKindRegistryIter<'a> {
|
|
||||||
type Item = &'a DynJobKind;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.0.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
self.0.size_hint()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count(self) -> usize
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.0.count()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last(self) -> Option<Self::Item> {
|
|
||||||
self.0.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
|
||||||
self.0.nth(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold<B, F>(self, init: B, f: F) -> B
|
|
||||||
where
|
|
||||||
F: FnMut(B, Self::Item) -> B,
|
|
||||||
{
|
|
||||||
self.0.fold(init, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> std::iter::FusedIterator for JobKindRegistryIter<'a> {}
|
|
||||||
|
|
||||||
impl<'a> ExactSizeIterator for JobKindRegistryIter<'a> {}
|
|
||||||
|
|
||||||
impl<'a> DoubleEndedIterator for JobKindRegistryIter<'a> {
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
self.0.next_back()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
|
||||||
self.0.nth_back(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rfold<B, F>(self, init: B, f: F) -> B
|
|
||||||
where
|
|
||||||
F: FnMut(B, Self::Item) -> B,
|
|
||||||
{
|
|
||||||
self.0.rfold(init, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct JobKindRegistryIterWithNames<'a>(
|
|
||||||
std::collections::btree_map::Iter<'a, InternedStrCompareAsStr, DynJobKind>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<'a> Iterator for JobKindRegistryIterWithNames<'a> {
|
|
||||||
type Item = (Interned<str>, &'a DynJobKind);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.0.next().map(|(name, job_kind)| (name.0, job_kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
self.0.size_hint()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count(self) -> usize
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.0.count()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last(self) -> Option<Self::Item> {
|
|
||||||
self.0.last().map(|(name, job_kind)| (name.0, job_kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
|
||||||
self.0.nth(n).map(|(name, job_kind)| (name.0, job_kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold<B, F>(self, init: B, f: F) -> B
|
|
||||||
where
|
|
||||||
F: FnMut(B, Self::Item) -> B,
|
|
||||||
{
|
|
||||||
self.0
|
|
||||||
.map(|(name, job_kind)| (name.0, job_kind))
|
|
||||||
.fold(init, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> std::iter::FusedIterator for JobKindRegistryIterWithNames<'a> {}
|
|
||||||
|
|
||||||
impl<'a> ExactSizeIterator for JobKindRegistryIterWithNames<'a> {}
|
|
||||||
|
|
||||||
impl<'a> DoubleEndedIterator for JobKindRegistryIterWithNames<'a> {
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
self.0
|
|
||||||
.next_back()
|
|
||||||
.map(|(name, job_kind)| (name.0, job_kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
|
||||||
self.0
|
|
||||||
.nth_back(n)
|
|
||||||
.map(|(name, job_kind)| (name.0, job_kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rfold<B, F>(self, init: B, f: F) -> B
|
|
||||||
where
|
|
||||||
F: FnMut(B, Self::Item) -> B,
|
|
||||||
{
|
|
||||||
self.0
|
|
||||||
.map(|(name, job_kind)| (name.0, job_kind))
|
|
||||||
.rfold(init, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
pub fn register_job_kind<K: JobKind>(kind: K) {
|
|
||||||
DynJobKind::new(kind).register();
|
|
||||||
}
|
|
||||||
|
|
@ -1,418 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
build::{
|
|
||||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GetJobPositionJob,
|
|
||||||
GlobalParams, JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem,
|
|
||||||
JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
|
||||||
external::{
|
|
||||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
|
||||||
},
|
|
||||||
firrtl::{Firrtl, FirrtlJobKind},
|
|
||||||
},
|
|
||||||
intern::{Intern, InternSlice, Interned},
|
|
||||||
util::job_server::AcquiredJob,
|
|
||||||
};
|
|
||||||
use clap::Args;
|
|
||||||
use eyre::{Context, bail};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::{
|
|
||||||
ffi::{OsStr, OsString},
|
|
||||||
fmt, mem,
|
|
||||||
path::Path,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// based on [LLVM Circt's recommended lowering options][lowering-options]
|
|
||||||
///
|
|
||||||
/// [lowering-options]: https://circt.llvm.org/docs/VerilogGeneration/#recommended-loweringoptions-by-target
|
|
||||||
#[derive(clap::ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum VerilogDialect {
|
|
||||||
Questa,
|
|
||||||
Spyglass,
|
|
||||||
Verilator,
|
|
||||||
Vivado,
|
|
||||||
Yosys,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for VerilogDialect {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VerilogDialect {
|
|
||||||
pub fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
VerilogDialect::Questa => "questa",
|
|
||||||
VerilogDialect::Spyglass => "spyglass",
|
|
||||||
VerilogDialect::Verilator => "verilator",
|
|
||||||
VerilogDialect::Vivado => "vivado",
|
|
||||||
VerilogDialect::Yosys => "yosys",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn firtool_extra_args(self) -> &'static [&'static str] {
|
|
||||||
match self {
|
|
||||||
VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"],
|
|
||||||
VerilogDialect::Spyglass => {
|
|
||||||
&["--lowering-options=explicitBitcast,disallowExpressionInliningInPorts"]
|
|
||||||
}
|
|
||||||
VerilogDialect::Verilator => &[
|
|
||||||
"--lowering-options=locationInfoStyle=wrapInAtSquareBracket,disallowLocalVariables",
|
|
||||||
],
|
|
||||||
VerilogDialect::Vivado => &["--lowering-options=mitigateVivadoArrayIndexConstPropBug"],
|
|
||||||
VerilogDialect::Yosys => {
|
|
||||||
&["--lowering-options=disallowLocalVariables,disallowPackedArrays"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args, Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct UnadjustedVerilogArgs {
|
|
||||||
#[arg(long = "firtool-extra-arg", value_name = "ARG")]
|
|
||||||
pub firtool_extra_args: Vec<OsString>,
|
|
||||||
/// adapt the generated Verilog for a particular toolchain
|
|
||||||
#[arg(long)]
|
|
||||||
pub verilog_dialect: Option<VerilogDialect>,
|
|
||||||
#[arg(long)]
|
|
||||||
pub verilog_debug: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToArgs for UnadjustedVerilogArgs {
|
|
||||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
|
||||||
let Self {
|
|
||||||
ref firtool_extra_args,
|
|
||||||
verilog_dialect,
|
|
||||||
verilog_debug,
|
|
||||||
} = *self;
|
|
||||||
for arg in firtool_extra_args {
|
|
||||||
args.write_long_option_eq("firtool-extra-arg", arg);
|
|
||||||
}
|
|
||||||
if let Some(verilog_dialect) = verilog_dialect {
|
|
||||||
args.write_long_option_eq("verilog-dialect", verilog_dialect.as_str());
|
|
||||||
}
|
|
||||||
if verilog_debug {
|
|
||||||
args.write_arg("--verilog-debug");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
|
||||||
pub struct Firtool;
|
|
||||||
|
|
||||||
impl ExternalProgramTrait for Firtool {
|
|
||||||
fn default_program_name() -> Interned<str> {
|
|
||||||
"firtool".intern()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Deserialize, Serialize)]
|
|
||||||
pub struct UnadjustedVerilog {
|
|
||||||
firrtl_file: Interned<Path>,
|
|
||||||
firrtl_file_name: Interned<OsStr>,
|
|
||||||
unadjusted_verilog_file: Interned<Path>,
|
|
||||||
unadjusted_verilog_file_name: Interned<OsStr>,
|
|
||||||
firtool_extra_args: Interned<[Interned<OsStr>]>,
|
|
||||||
verilog_dialect: Option<VerilogDialect>,
|
|
||||||
verilog_debug: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnadjustedVerilog {
|
|
||||||
pub fn firrtl_file(&self) -> Interned<Path> {
|
|
||||||
self.firrtl_file
|
|
||||||
}
|
|
||||||
pub fn unadjusted_verilog_file(&self) -> Interned<Path> {
|
|
||||||
self.unadjusted_verilog_file
|
|
||||||
}
|
|
||||||
pub fn firtool_extra_args(&self) -> Interned<[Interned<OsStr>]> {
|
|
||||||
self.firtool_extra_args
|
|
||||||
}
|
|
||||||
pub fn verilog_dialect(&self) -> Option<VerilogDialect> {
|
|
||||||
self.verilog_dialect
|
|
||||||
}
|
|
||||||
pub fn verilog_debug(&self) -> bool {
|
|
||||||
self.verilog_debug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExternalCommand for UnadjustedVerilog {
|
|
||||||
type AdditionalArgs = UnadjustedVerilogArgs;
|
|
||||||
type AdditionalJobData = UnadjustedVerilog;
|
|
||||||
type BaseJobPosition = GetJobPositionDependencies<GetJobPositionJob>;
|
|
||||||
type Dependencies = JobKindAndDependencies<FirrtlJobKind>;
|
|
||||||
type ExternalProgram = Firtool;
|
|
||||||
|
|
||||||
fn dependencies() -> Self::Dependencies {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn args_to_jobs(
|
|
||||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
|
||||||
params: &JobParams,
|
|
||||||
global_params: &GlobalParams,
|
|
||||||
) -> eyre::Result<(
|
|
||||||
Self::AdditionalJobData,
|
|
||||||
<Self::Dependencies as JobDependencies>::JobsAndKinds,
|
|
||||||
)> {
|
|
||||||
args.args_to_jobs_external_simple(params, global_params, |args, dependencies| {
|
|
||||||
let UnadjustedVerilogArgs {
|
|
||||||
firtool_extra_args,
|
|
||||||
verilog_dialect,
|
|
||||||
verilog_debug,
|
|
||||||
} = args.additional_args;
|
|
||||||
let unadjusted_verilog_file = dependencies
|
|
||||||
.dependencies
|
|
||||||
.job
|
|
||||||
.job
|
|
||||||
.file_with_ext("unadjusted.v");
|
|
||||||
let firrtl_job = dependencies.get_job::<Firrtl, _>();
|
|
||||||
Ok(UnadjustedVerilog {
|
|
||||||
firrtl_file: firrtl_job.firrtl_file(),
|
|
||||||
firrtl_file_name: firrtl_job
|
|
||||||
.firrtl_file()
|
|
||||||
.interned_file_name()
|
|
||||||
.expect("known to have file name"),
|
|
||||||
unadjusted_verilog_file,
|
|
||||||
unadjusted_verilog_file_name: unadjusted_verilog_file
|
|
||||||
.interned_file_name()
|
|
||||||
.expect("known to have file name"),
|
|
||||||
firtool_extra_args: firtool_extra_args.into_iter().map(Interned::from).collect(),
|
|
||||||
verilog_dialect,
|
|
||||||
verilog_debug,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> {
|
|
||||||
[JobItemName::Path {
|
|
||||||
path: job.additional_job_data().firrtl_file,
|
|
||||||
}]
|
|
||||||
.intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
|
|
||||||
[job.additional_job_data().unadjusted_verilog_file].intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
|
|
||||||
let UnadjustedVerilog {
|
|
||||||
firrtl_file: _,
|
|
||||||
firrtl_file_name,
|
|
||||||
unadjusted_verilog_file: _,
|
|
||||||
unadjusted_verilog_file_name,
|
|
||||||
firtool_extra_args,
|
|
||||||
verilog_dialect,
|
|
||||||
verilog_debug,
|
|
||||||
} = *job.additional_job_data();
|
|
||||||
args.write_interned_arg(firrtl_file_name);
|
|
||||||
args.write_arg("-o");
|
|
||||||
args.write_interned_arg(unadjusted_verilog_file_name);
|
|
||||||
if verilog_debug {
|
|
||||||
args.write_args(["-g", "--preserve-values=named"]);
|
|
||||||
}
|
|
||||||
if let Some(dialect) = verilog_dialect {
|
|
||||||
args.write_args(dialect.firtool_extra_args().iter().copied());
|
|
||||||
}
|
|
||||||
args.write_interned_args(firtool_extra_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
|
|
||||||
Some(job.output_dir())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn job_kind_name() -> Interned<str> {
|
|
||||||
"unadjusted-verilog".intern()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subcommand_hidden() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_even_if_cached_arg_name() -> Interned<str> {
|
|
||||||
"firtool-run-even-if-cached".intern()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct VerilogJobKind;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Args)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct VerilogJobArgs {}
|
|
||||||
|
|
||||||
impl ToArgs for VerilogJobArgs {
|
|
||||||
fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) {
|
|
||||||
let Self {} = self;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
pub struct VerilogJob {
|
|
||||||
output_dir: Interned<Path>,
|
|
||||||
unadjusted_verilog_file: Interned<Path>,
|
|
||||||
main_verilog_file: Interned<Path>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VerilogJob {
|
|
||||||
pub fn output_dir(&self) -> Interned<Path> {
|
|
||||||
self.output_dir
|
|
||||||
}
|
|
||||||
pub fn unadjusted_verilog_file(&self) -> Interned<Path> {
|
|
||||||
self.unadjusted_verilog_file
|
|
||||||
}
|
|
||||||
pub fn main_verilog_file(&self) -> Interned<Path> {
|
|
||||||
self.main_verilog_file
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn unwrap_additional_files(additional_files: &JobItem) -> &[Interned<Path>] {
|
|
||||||
match additional_files {
|
|
||||||
JobItem::DynamicPaths {
|
|
||||||
paths,
|
|
||||||
source_job_name,
|
|
||||||
} if *source_job_name == VerilogJobKind.name() => paths,
|
|
||||||
v => panic!("expected VerilogJob's additional files JobItem: {v:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn all_verilog_files(
|
|
||||||
main_verilog_file: Interned<Path>,
|
|
||||||
additional_files: &[Interned<Path>],
|
|
||||||
) -> eyre::Result<Interned<[Interned<Path>]>> {
|
|
||||||
let mut retval = Vec::with_capacity(additional_files.len().saturating_add(1));
|
|
||||||
for verilog_file in [main_verilog_file].iter().chain(additional_files) {
|
|
||||||
if !["v", "sv"]
|
|
||||||
.iter()
|
|
||||||
.any(|extension| verilog_file.extension() == Some(extension.as_ref()))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let verilog_file = std::path::absolute(verilog_file).wrap_err_with(|| {
|
|
||||||
format!("converting {verilog_file:?} to an absolute path failed")
|
|
||||||
})?;
|
|
||||||
if verilog_file
|
|
||||||
.as_os_str()
|
|
||||||
.as_encoded_bytes()
|
|
||||||
.iter()
|
|
||||||
.any(|&ch| (ch != b' ' && ch != b'\t' && ch.is_ascii_whitespace()) || ch == b'"')
|
|
||||||
{
|
|
||||||
bail!("verilog file path contains characters that aren't permitted");
|
|
||||||
}
|
|
||||||
retval.push(verilog_file.intern_deref());
|
|
||||||
}
|
|
||||||
Ok(retval.intern_slice())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobKind for VerilogJobKind {
|
|
||||||
type Args = VerilogJobArgs;
|
|
||||||
type Job = VerilogJob;
|
|
||||||
type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<UnadjustedVerilog>>;
|
|
||||||
|
|
||||||
fn dependencies(self) -> Self::Dependencies {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn args_to_jobs(
|
|
||||||
args: JobArgsAndDependencies<Self>,
|
|
||||||
params: &JobParams,
|
|
||||||
global_params: &GlobalParams,
|
|
||||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
|
||||||
args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| {
|
|
||||||
let VerilogJobArgs {} = args;
|
|
||||||
let base_job = dependencies.get_job::<BaseJob, _>();
|
|
||||||
Ok(VerilogJob {
|
|
||||||
output_dir: base_job.output_dir(),
|
|
||||||
unadjusted_verilog_file: dependencies
|
|
||||||
.job
|
|
||||||
.job
|
|
||||||
.additional_job_data()
|
|
||||||
.unadjusted_verilog_file(),
|
|
||||||
main_verilog_file: base_job.file_with_ext("v"),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
|
|
||||||
[JobItemName::Path {
|
|
||||||
path: job.unadjusted_verilog_file,
|
|
||||||
}]
|
|
||||||
.intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
|
|
||||||
[
|
|
||||||
JobItemName::Path {
|
|
||||||
path: job.main_verilog_file,
|
|
||||||
},
|
|
||||||
JobItemName::DynamicPaths {
|
|
||||||
source_job_name: self.name(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.intern_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(self) -> Interned<str> {
|
|
||||||
"verilog".intern()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
self,
|
|
||||||
job: &Self::Job,
|
|
||||||
inputs: &[JobItem],
|
|
||||||
_params: &JobParams,
|
|
||||||
_global_params: &GlobalParams,
|
|
||||||
_acquired_job: &mut AcquiredJob,
|
|
||||||
) -> eyre::Result<Vec<JobItem>> {
|
|
||||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
|
||||||
let input = std::fs::read_to_string(job.unadjusted_verilog_file())?;
|
|
||||||
let file_separator_prefix = "\n// ----- 8< ----- FILE \"";
|
|
||||||
let file_separator_suffix = "\" ----- 8< -----\n\n";
|
|
||||||
let mut input = &*input;
|
|
||||||
let main_verilog_file = job.main_verilog_file();
|
|
||||||
let mut file_name = Some(main_verilog_file);
|
|
||||||
let mut additional_outputs = Vec::new();
|
|
||||||
loop {
|
|
||||||
let (chunk, next_file_name) = if let Some((chunk, rest)) =
|
|
||||||
input.split_once(file_separator_prefix)
|
|
||||||
{
|
|
||||||
let Some((next_file_name, rest)) = rest.split_once(file_separator_suffix) else {
|
|
||||||
bail!(
|
|
||||||
"parsing firtool's output failed: found {file_separator_prefix:?} but no {file_separator_suffix:?}"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
input = rest;
|
|
||||||
let next_file_name = job.output_dir.join(next_file_name).intern_deref();
|
|
||||||
additional_outputs.push(next_file_name);
|
|
||||||
(chunk, Some(next_file_name))
|
|
||||||
} else {
|
|
||||||
(mem::take(&mut input), None)
|
|
||||||
};
|
|
||||||
let Some(file_name) = mem::replace(&mut file_name, next_file_name) else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
std::fs::write(&file_name, chunk)?;
|
|
||||||
}
|
|
||||||
Ok(vec![
|
|
||||||
JobItem::Path {
|
|
||||||
path: main_verilog_file,
|
|
||||||
},
|
|
||||||
JobItem::DynamicPaths {
|
|
||||||
paths: additional_outputs,
|
|
||||||
source_job_name: self.name(),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
|
|
||||||
[
|
|
||||||
DynJobKind::new(ExternalCommandJobKind::<UnadjustedVerilog>::new()),
|
|
||||||
DynJobKind::new(VerilogJobKind),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -3,24 +3,22 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{
|
expr::{
|
||||||
CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType,
|
CastToBits, Expr, ReduceBits, ToExpr,
|
||||||
Valueless,
|
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
|
||||||
ops::{ArrayLiteral, BundleLiteral, StructuralEq},
|
|
||||||
value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue},
|
|
||||||
},
|
},
|
||||||
int::{Bool, DynSize},
|
int::{Bool, DynSize},
|
||||||
intern::{Intern, InternSlice, Interned},
|
intern::{Intern, Interned},
|
||||||
sim::value::{SimValue, SimValueEq, ToSimValue, ToSimValueWithType},
|
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{
|
ty::{
|
||||||
CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize,
|
CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize,
|
||||||
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug,
|
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type,
|
||||||
StaticType, Type, TypeProperties, TypeWithDeref, impl_match_variant_as_self,
|
TypeProperties, TypeWithDeref, impl_match_variant_as_self,
|
||||||
},
|
},
|
||||||
util::HashMap,
|
util::HashMap,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{borrow::Cow, fmt, marker::PhantomData};
|
use std::{fmt, marker::PhantomData};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct BundleField {
|
pub struct BundleField {
|
||||||
|
|
@ -271,17 +269,9 @@ impl Type for Bundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimValueDebug for Bundle {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait BundleType: Type<BaseType = Bundle> {
|
pub trait BundleType: Type<BaseType = Bundle> {
|
||||||
type Builder: Default;
|
type Builder: Default;
|
||||||
|
type FilledBuilder: ToExpr<Type = Self>;
|
||||||
fn fields(&self) -> Interned<[BundleField]>;
|
fn fields(&self) -> Interned<[BundleField]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,7 +318,7 @@ impl<'a> BundleSimValueFromOpaque<'a> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn field_clone_from_opaque<T: Type>(&mut self, field_value: &mut SimValue<T>) {
|
pub fn field_clone_from_opaque<T: Type>(&mut self, field_value: &mut SimValue<T>) {
|
||||||
let (field_ty, field_opaque) = self.field_ty_and_opaque::<T>();
|
let (field_ty, field_opaque) = self.field_ty_and_opaque::<T>();
|
||||||
assert_eq!(field_ty, field_value.ty());
|
assert_eq!(field_ty, SimValue::ty(field_value));
|
||||||
SimValue::opaque_mut(field_value).clone_from_slice(field_opaque);
|
SimValue::opaque_mut(field_value).clone_from_slice(field_opaque);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -364,7 +354,7 @@ impl<'a> BundleSimValueToOpaque<'a> {
|
||||||
else {
|
else {
|
||||||
panic!("tried to write too many fields with BundleSimValueToOpaque");
|
panic!("tried to write too many fields with BundleSimValueToOpaque");
|
||||||
};
|
};
|
||||||
assert_eq!(T::from_canonical(ty), field_value.ty());
|
assert_eq!(T::from_canonical(ty), SimValue::ty(field_value));
|
||||||
self.writer.fill_prefix_with(ty.size(), |writer| {
|
self.writer.fill_prefix_with(ty.size(), |writer| {
|
||||||
writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice())
|
writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice())
|
||||||
});
|
});
|
||||||
|
|
@ -384,8 +374,17 @@ impl<'a> BundleSimValueToOpaque<'a> {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NoBuilder;
|
pub struct NoBuilder;
|
||||||
|
|
||||||
|
pub struct Unfilled<T: Type>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: Type> Default for Unfilled<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BundleType for Bundle {
|
impl BundleType for Bundle {
|
||||||
type Builder = NoBuilder;
|
type Builder = NoBuilder;
|
||||||
|
type FilledBuilder = Expr<Bundle>;
|
||||||
fn fields(&self) -> Interned<[BundleField]> {
|
fn fields(&self) -> Interned<[BundleField]> {
|
||||||
self.0.fields
|
self.0.fields
|
||||||
}
|
}
|
||||||
|
|
@ -421,14 +420,15 @@ macro_rules! impl_tuple_builder_fields {
|
||||||
) => {
|
) => {
|
||||||
impl<
|
impl<
|
||||||
$($head_type_var,)*
|
$($head_type_var,)*
|
||||||
|
$cur_type_var: Type,
|
||||||
$($tail_type_var,)*
|
$($tail_type_var,)*
|
||||||
> TupleBuilder<(
|
> TupleBuilder<(
|
||||||
$($head_type_var,)*
|
$($head_type_var,)*
|
||||||
(),
|
Unfilled<$cur_type_var>,
|
||||||
$($tail_type_var,)*
|
$($tail_type_var,)*
|
||||||
)>
|
)>
|
||||||
{
|
{
|
||||||
pub fn $cur_field<$cur_type_var: Type>(self, $cur_var: impl ToExpr<Type = $cur_type_var>) -> TupleBuilder<(
|
pub fn $cur_field(self, $cur_var: impl ToExpr<Type = $cur_type_var>) -> TupleBuilder<(
|
||||||
$($head_type_var,)*
|
$($head_type_var,)*
|
||||||
Expr<$cur_type_var>,
|
Expr<$cur_type_var>,
|
||||||
$($tail_type_var,)*
|
$($tail_type_var,)*
|
||||||
|
|
@ -452,12 +452,6 @@ macro_rules! impl_tuple_builder_fields {
|
||||||
($global:tt []) => {};
|
($global:tt []) => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! get_unit_ty {
|
|
||||||
($($tt:tt)*) => {
|
|
||||||
()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_tuples {
|
macro_rules! impl_tuples {
|
||||||
(
|
(
|
||||||
[$({
|
[$({
|
||||||
|
|
@ -480,14 +474,6 @@ macro_rules! impl_tuples {
|
||||||
#[var($var)]
|
#[var($var)]
|
||||||
})*]
|
})*]
|
||||||
}
|
}
|
||||||
impl<$($T: Type,)*> SimValueDebug for ($($T,)*) {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<$($T: Type,)*> Type for ($($T,)*) {
|
impl<$($T: Type,)*> Type for ($($T,)*) {
|
||||||
type BaseType = Bundle;
|
type BaseType = Bundle;
|
||||||
type MaskType = ($($T::MaskType,)*);
|
type MaskType = ($($T::MaskType,)*);
|
||||||
|
|
@ -559,10 +545,11 @@ macro_rules! impl_tuples {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<$($T: Type,)*> BundleType for ($($T,)*) {
|
impl<$($T: Type,)*> BundleType for ($($T,)*) {
|
||||||
type Builder = TupleBuilder<($(get_unit_ty!($T),)*)>;
|
type Builder = TupleBuilder<($(Unfilled<$T>,)*)>;
|
||||||
|
type FilledBuilder = TupleBuilder<($(Expr<$T>,)*)>;
|
||||||
fn fields(&self) -> Interned<[BundleField]> {
|
fn fields(&self) -> Interned<[BundleField]> {
|
||||||
let ($($var,)*) = self;
|
let ($($var,)*) = self;
|
||||||
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*].intern_slice()
|
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
|
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
|
||||||
|
|
@ -585,56 +572,25 @@ macro_rules! impl_tuples {
|
||||||
builder.finish()
|
builder.finish()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
impl<'a, $($T: ToSimValue,)*> ToSimValueInner<'a> for ($($T,)*)
|
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
|
||||||
where
|
|
||||||
Self: ValueType<Type = ($($T::Type,)*)>,
|
|
||||||
{
|
|
||||||
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
|
|
||||||
let ($($var,)*) = this;
|
|
||||||
Cow::Owned(($($var.to_sim_value(),)*))
|
|
||||||
}
|
|
||||||
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
|
|
||||||
let ($($var,)*) = this;
|
|
||||||
Cow::Owned(($($var.into_sim_value(),)*))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<$($T: ValueType,)*> ValueType for ($($T,)*)
|
|
||||||
where
|
|
||||||
ValueCategoryValue: ValueCategoryCommon<($($T::ValueCategory,)*)>,
|
|
||||||
{
|
|
||||||
type Type = ($($T::Type,)*);
|
type Type = ($($T::Type,)*);
|
||||||
type ValueCategory = <ValueCategoryValue as ValueCategoryCommon<($($T::ValueCategory,)*)>>::Common;
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
let ($($var,)*) = self;
|
|
||||||
($($var.ty(),)*)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*)
|
|
||||||
where
|
|
||||||
Self: ValueType<Type = ($($T::Type,)*)>,
|
|
||||||
{
|
|
||||||
fn to_expr(&self) -> Expr<Self::Type> {
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
let ($($var,)*) = self;
|
let ($($var,)*) = self;
|
||||||
$(let $var = $var.to_expr();)*
|
$(let $var = $var.to_expr();)*
|
||||||
let ty = ($($var.ty(),)*);
|
let ty = ($(Expr::ty($var),)*);
|
||||||
let field_values = [$(Expr::canonical($var)),*];
|
let field_values = [$(Expr::canonical($var)),*];
|
||||||
BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
|
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<$($T: Type,)*> ValueType for TupleBuilder<($(Expr<$T>,)*)> {
|
|
||||||
type Type = ($($T,)*);
|
|
||||||
type ValueCategory = ValueCategoryExpr;
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
let ($($var,)*) = self.0;
|
|
||||||
($($var.ty(),)*)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
|
impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
|
||||||
|
type Type = ($($T,)*);
|
||||||
|
|
||||||
fn to_expr(&self) -> Expr<Self::Type> {
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
let ($($var,)*) = self.0;
|
let ($($var,)*) = self.0;
|
||||||
let ty = ($($var.ty(),)*);
|
let ty = ($(Expr::ty($var),)*);
|
||||||
let field_values = [$(Expr::canonical($var)),*];
|
let field_values = [$(Expr::canonical($var)),*];
|
||||||
BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
|
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) {
|
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) {
|
||||||
|
|
@ -668,7 +624,7 @@ macro_rules! impl_tuples {
|
||||||
};
|
};
|
||||||
let mut opaque = OpaqueSimValue::empty();
|
let mut opaque = OpaqueSimValue::empty();
|
||||||
$(let $var = $var.into_sim_value_with_type($ty_var.ty);
|
$(let $var = $var.into_sim_value_with_type($ty_var.ty);
|
||||||
assert_eq!($var.ty(), $ty_var.ty);
|
assert_eq!(SimValue::ty(&$var), $ty_var.ty);
|
||||||
opaque.extend_from_slice(SimValue::opaque(&$var).as_slice());
|
opaque.extend_from_slice(SimValue::opaque(&$var).as_slice());
|
||||||
)*
|
)*
|
||||||
SimValue::from_opaque(ty, opaque)
|
SimValue::from_opaque(ty, opaque)
|
||||||
|
|
@ -690,94 +646,53 @@ macro_rules! impl_tuples {
|
||||||
SimValue::from_value(ty, ($($var,)*))
|
SimValue::from_value(ty, ($($var,)*))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*)
|
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) {
|
||||||
where
|
type Type = ($($T::Type,)*);
|
||||||
Self: ValueType<Type = ($($T::Type,)*)>,
|
|
||||||
{
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
let ($($var,)*) = self;
|
let ($($var,)*) = self;
|
||||||
$(let $var = $var.to_sim_value();)*
|
$(let $var = $var.to_sim_value();)*
|
||||||
SimValue::from_value(($($var.ty(),)*), ($($var,)*))
|
SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn into_sim_value(self) -> SimValue<Self::Type> {
|
fn into_sim_value(self) -> SimValue<Self::Type> {
|
||||||
let ($($var,)*) = self;
|
let ($($var,)*) = self;
|
||||||
$(let $var = $var.to_sim_value();)*
|
$(let $var = $var.to_sim_value();)*
|
||||||
SimValue::from_value(($($var.ty(),)*), ($($var,)*))
|
SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<$($Lhs: Type + HdlPartialEqImpl<$Rhs>, $Rhs: Type,)*> HdlPartialEqImpl<($($Rhs,)*)> for ($($Lhs,)*) {
|
|
||||||
const TRY_STRUCTURAL_EQ: bool = true $(&& <$Lhs as HdlPartialEqImpl<$Rhs>>::TRY_STRUCTURAL_EQ)*;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_eq(
|
|
||||||
lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
rhs: ($($Rhs,)*),
|
|
||||||
rhs_value: Cow<'_, <($($Rhs,)*) as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
let ($($lhs_var,)*) = &*lhs_value;
|
|
||||||
let ($($rhs_var,)*) = &*rhs_value;
|
|
||||||
let retval = true;
|
|
||||||
$(let retval = retval && $Lhs::cmp_value_eq(lhs.$num, Cow::Borrowed($lhs_var), rhs.$num, Cow::Borrowed($rhs_var));)*
|
|
||||||
retval
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
|
||||||
if Self::TRY_STRUCTURAL_EQ {
|
|
||||||
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
|
|
||||||
return retval.to_expr();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
|
||||||
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||||
let ($($lhs_var,)*) = *lhs;
|
let ($($lhs_var,)*) = *lhs;
|
||||||
let ($($rhs_var,)*) = *rhs;
|
let ($($rhs_var,)*) = *rhs;
|
||||||
ArrayLiteral::<Bool, DynSize>::new(
|
ArrayLiteral::<Bool, DynSize>::new(
|
||||||
Bool,
|
Bool,
|
||||||
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_eq($lhs_var, $rhs_var)),)*]),
|
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
|
||||||
)
|
)
|
||||||
.cast_to_bits()
|
.cast_to_bits()
|
||||||
.all_one_bits()
|
.all_one_bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
|
||||||
if Self::TRY_STRUCTURAL_EQ {
|
|
||||||
if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) {
|
|
||||||
return !retval.to_expr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let ($($lhs_var,)*) = *lhs;
|
let ($($lhs_var,)*) = *lhs;
|
||||||
let ($($rhs_var,)*) = *rhs;
|
let ($($rhs_var,)*) = *rhs;
|
||||||
ArrayLiteral::<Bool, DynSize>::new(
|
ArrayLiteral::<Bool, DynSize>::new(
|
||||||
Bool,
|
Bool,
|
||||||
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_ne($lhs_var, $rhs_var)),)*]),
|
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
|
||||||
)
|
)
|
||||||
.cast_to_bits()
|
.cast_to_bits()
|
||||||
.any_one_bits()
|
.any_one_bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_valueless_eq(lhs: Valueless<Self>, rhs: Valueless<($($Rhs,)*)>) -> Valueless<Bool> {
|
|
||||||
let ($($lhs_var,)*) = lhs.ty();
|
|
||||||
let ($($rhs_var,)*) = rhs.ty();
|
|
||||||
// let them check that the types can be compared
|
|
||||||
$($Lhs::cmp_valueless_eq(Valueless::new($lhs_var), Valueless::new($rhs_var));)*
|
|
||||||
Valueless::new(Bool)
|
|
||||||
}
|
}
|
||||||
|
impl<$($Lhs: SimValuePartialEq<$Rhs>, $Rhs: Type,)*> SimValuePartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
|
||||||
#[track_caller]
|
fn sim_value_eq(lhs: &SimValue<Self>, rhs: &SimValue<($($Rhs,)*)>) -> bool {
|
||||||
fn cmp_valueless_ne(lhs: Valueless<Self>, rhs: Valueless<($($Rhs,)*)>) -> Valueless<Bool> {
|
let ($($lhs_var,)*) = &**lhs;
|
||||||
let ($($lhs_var,)*) = lhs.ty();
|
let ($($rhs_var,)*) = &**rhs;
|
||||||
let ($($rhs_var,)*) = rhs.ty();
|
let retval = true;
|
||||||
// let them check that the types can be compared
|
$(let retval = retval && $lhs_var == $rhs_var;)*
|
||||||
$($Lhs::cmp_valueless_ne(Valueless::new($lhs_var), Valueless::new($rhs_var));)*
|
retval
|
||||||
Valueless::new(Bool)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<$($T: SimValueEq + HdlPartialEqImpl<$T>,)*> SimValueEq for ($($T,)*) {}
|
|
||||||
};
|
};
|
||||||
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
||||||
impl_tuples!([$($lhs)*] []);
|
impl_tuples!([$($lhs)*] []);
|
||||||
|
|
@ -802,15 +717,6 @@ impl_tuples! {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> SimValueDebug for PhantomData<T> {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
|
impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
|
||||||
type BaseType = Bundle;
|
type BaseType = Bundle;
|
||||||
type MaskType = ();
|
type MaskType = ();
|
||||||
|
|
@ -875,16 +781,9 @@ impl<T: ?Sized + Send + Sync + 'static> Default for PhantomDataBuilder<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> ValueType for PhantomDataBuilder<T> {
|
|
||||||
type Type = PhantomData<T>;
|
|
||||||
type ValueCategory = ValueCategoryValue;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
|
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
|
||||||
|
type Type = PhantomData<T>;
|
||||||
|
|
||||||
fn to_expr(&self) -> Expr<Self::Type> {
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
PhantomData.to_expr()
|
PhantomData.to_expr()
|
||||||
}
|
}
|
||||||
|
|
@ -892,6 +791,7 @@ impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> BundleType for PhantomData<T> {
|
impl<T: ?Sized + Send + Sync + 'static> BundleType for PhantomData<T> {
|
||||||
type Builder = PhantomDataBuilder<T>;
|
type Builder = PhantomDataBuilder<T>;
|
||||||
|
type FilledBuilder = PhantomDataBuilder<T>;
|
||||||
fn fields(&self) -> Interned<[BundleField]> {
|
fn fields(&self) -> Interned<[BundleField]> {
|
||||||
Interned::default()
|
Interned::default()
|
||||||
}
|
}
|
||||||
|
|
@ -910,22 +810,17 @@ impl<T: ?Sized + Send + Sync + 'static> StaticType for PhantomData<T> {
|
||||||
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> ValueType for PhantomData<T> {
|
|
||||||
type Type = PhantomData<T>;
|
|
||||||
type ValueCategory = ValueCategoryValue;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
|
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
|
||||||
|
type Type = PhantomData<T>;
|
||||||
|
|
||||||
fn to_expr(&self) -> Expr<Self::Type> {
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
|
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> {
|
impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> {
|
||||||
|
type Type = PhantomData<T>;
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_sim_value(&self) -> SimValue<Self> {
|
fn to_sim_value(&self) -> SimValue<Self> {
|
||||||
SimValue::from_value(*self, *self)
|
SimValue::from_value(*self, *self)
|
||||||
|
|
|
||||||
806
crates/fayalite/src/cli.rs
Normal file
806
crates/fayalite/src/cli.rs
Normal file
|
|
@ -0,0 +1,806 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
use crate::{
|
||||||
|
bundle::{Bundle, BundleType},
|
||||||
|
firrtl::{self, ExportOptions},
|
||||||
|
intern::Interned,
|
||||||
|
module::Module,
|
||||||
|
util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8},
|
||||||
|
};
|
||||||
|
use clap::{
|
||||||
|
Parser, Subcommand, ValueEnum, ValueHint,
|
||||||
|
builder::{OsStringValueParser, TypedValueParser},
|
||||||
|
};
|
||||||
|
use eyre::{Report, eyre};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
error,
|
||||||
|
ffi::OsString,
|
||||||
|
fmt::{self, Write},
|
||||||
|
fs, io, mem,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
pub type Result<T = (), E = CliError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
pub struct CliError(Report);
|
||||||
|
|
||||||
|
impl fmt::Debug for CliError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CliError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for CliError {}
|
||||||
|
|
||||||
|
impl From<io::Error> for CliError {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
CliError(Report::new(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RunPhase<Arg> {
|
||||||
|
type Output;
|
||||||
|
fn run(&self, arg: Arg) -> Result<Self::Output> {
|
||||||
|
self.run_with_job(arg, &mut AcquiredJob::acquire())
|
||||||
|
}
|
||||||
|
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct BaseArgs {
|
||||||
|
/// the directory to put the generated main output file and associated files in
|
||||||
|
#[arg(short, long, value_hint = ValueHint::DirPath, required = true)]
|
||||||
|
pub output: Option<PathBuf>,
|
||||||
|
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
|
||||||
|
#[arg(long)]
|
||||||
|
pub file_stem: Option<String>,
|
||||||
|
#[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")]
|
||||||
|
pub keep_temp_dir: bool,
|
||||||
|
#[arg(skip = false)]
|
||||||
|
pub redirect_output_for_rust_test: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BaseArgs {
|
||||||
|
fn make_firrtl_file_backend(&self) -> Result<(firrtl::FileBackend, Option<TempDir>)> {
|
||||||
|
let (dir_path, temp_dir) = match &self.output {
|
||||||
|
Some(output) => (output.clone(), None),
|
||||||
|
None => {
|
||||||
|
let temp_dir = TempDir::new()?;
|
||||||
|
if self.keep_temp_dir {
|
||||||
|
let temp_dir = temp_dir.into_path();
|
||||||
|
println!("created temporary directory: {}", temp_dir.display());
|
||||||
|
(temp_dir, None)
|
||||||
|
} else {
|
||||||
|
(temp_dir.path().to_path_buf(), Some(temp_dir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok((
|
||||||
|
firrtl::FileBackend {
|
||||||
|
dir_path,
|
||||||
|
top_fir_file_stem: self.file_stem.clone(),
|
||||||
|
circuit_name: None,
|
||||||
|
},
|
||||||
|
temp_dir,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
/// handles possibly redirecting the command's output for Rust tests
|
||||||
|
pub fn run_external_command(
|
||||||
|
&self,
|
||||||
|
_acquired_job: &mut AcquiredJob,
|
||||||
|
mut command: process::Command,
|
||||||
|
mut captured_output: Option<&mut String>,
|
||||||
|
) -> io::Result<process::ExitStatus> {
|
||||||
|
if self.redirect_output_for_rust_test || captured_output.is_some() {
|
||||||
|
let (reader, writer) = os_pipe::pipe()?;
|
||||||
|
let mut reader = io::BufReader::new(reader);
|
||||||
|
command.stderr(writer.try_clone()?);
|
||||||
|
command.stdout(writer); // must not leave writer around after spawning child
|
||||||
|
command.stdin(process::Stdio::null());
|
||||||
|
let mut child = command.spawn()?;
|
||||||
|
drop(command); // close writers
|
||||||
|
Ok(loop {
|
||||||
|
let status = child.try_wait()?;
|
||||||
|
streaming_read_utf8(&mut reader, |s| {
|
||||||
|
if let Some(captured_output) = captured_output.as_deref_mut() {
|
||||||
|
captured_output.push_str(s);
|
||||||
|
}
|
||||||
|
// use print! so output goes to Rust test output capture
|
||||||
|
print!("{s}");
|
||||||
|
io::Result::Ok(())
|
||||||
|
})?;
|
||||||
|
if let Some(status) = status {
|
||||||
|
break status;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
command.status()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct FirrtlArgs {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub base: BaseArgs,
|
||||||
|
#[command(flatten)]
|
||||||
|
pub export_options: ExportOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct FirrtlOutput {
|
||||||
|
pub file_stem: String,
|
||||||
|
pub top_module: String,
|
||||||
|
pub output_dir: PathBuf,
|
||||||
|
pub temp_dir: Option<TempDir>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FirrtlOutput {
|
||||||
|
pub fn file_with_ext(&self, ext: &str) -> PathBuf {
|
||||||
|
let mut retval = self.output_dir.join(&self.file_stem);
|
||||||
|
retval.set_extension(ext);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
pub fn firrtl_file(&self) -> PathBuf {
|
||||||
|
self.file_with_ext("fir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FirrtlArgs {
|
||||||
|
fn run_impl(
|
||||||
|
&self,
|
||||||
|
top_module: Module<Bundle>,
|
||||||
|
_acquired_job: &mut AcquiredJob,
|
||||||
|
) -> Result<FirrtlOutput> {
|
||||||
|
let (file_backend, temp_dir) = self.base.make_firrtl_file_backend()?;
|
||||||
|
let firrtl::FileBackend {
|
||||||
|
top_fir_file_stem,
|
||||||
|
circuit_name,
|
||||||
|
dir_path,
|
||||||
|
} = firrtl::export(file_backend, &top_module, self.export_options)?;
|
||||||
|
Ok(FirrtlOutput {
|
||||||
|
file_stem: top_fir_file_stem.expect(
|
||||||
|
"export is known to set the file stem from the circuit name if not provided",
|
||||||
|
),
|
||||||
|
top_module: circuit_name.expect("export is known to set the circuit name"),
|
||||||
|
output_dir: dir_path,
|
||||||
|
temp_dir,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BundleType> RunPhase<Module<T>> for FirrtlArgs {
|
||||||
|
type Output = FirrtlOutput;
|
||||||
|
fn run_with_job(
|
||||||
|
&self,
|
||||||
|
top_module: Module<T>,
|
||||||
|
acquired_job: &mut AcquiredJob,
|
||||||
|
) -> Result<Self::Output> {
|
||||||
|
self.run_impl(top_module.canonical(), acquired_job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BundleType> RunPhase<Interned<Module<T>>> for FirrtlArgs {
|
||||||
|
type Output = FirrtlOutput;
|
||||||
|
fn run_with_job(
|
||||||
|
&self,
|
||||||
|
top_module: Interned<Module<T>>,
|
||||||
|
acquired_job: &mut AcquiredJob,
|
||||||
|
) -> Result<Self::Output> {
|
||||||
|
self.run_with_job(*top_module, acquired_job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// based on [LLVM Circt's recommended lowering options
|
||||||
|
/// ](https://circt.llvm.org/docs/VerilogGeneration/#recommended-loweringoptions-by-target)
|
||||||
|
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum VerilogDialect {
|
||||||
|
Questa,
|
||||||
|
Spyglass,
|
||||||
|
Verilator,
|
||||||
|
Vivado,
|
||||||
|
Yosys,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for VerilogDialect {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerilogDialect {
|
||||||
|
pub fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
VerilogDialect::Questa => "questa",
|
||||||
|
VerilogDialect::Spyglass => "spyglass",
|
||||||
|
VerilogDialect::Verilator => "verilator",
|
||||||
|
VerilogDialect::Vivado => "vivado",
|
||||||
|
VerilogDialect::Yosys => "yosys",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn firtool_extra_args(self) -> &'static [&'static str] {
|
||||||
|
match self {
|
||||||
|
VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"],
|
||||||
|
VerilogDialect::Spyglass => {
|
||||||
|
&["--lowering-options=explicitBitcast,disallowExpressionInliningInPorts"]
|
||||||
|
}
|
||||||
|
VerilogDialect::Verilator => &[
|
||||||
|
"--lowering-options=locationInfoStyle=wrapInAtSquareBracket,disallowLocalVariables",
|
||||||
|
],
|
||||||
|
VerilogDialect::Vivado => &["--lowering-options=mitigateVivadoArrayIndexConstPropBug"],
|
||||||
|
VerilogDialect::Yosys => {
|
||||||
|
&["--lowering-options=disallowLocalVariables,disallowPackedArrays"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct VerilogArgs {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub firrtl: FirrtlArgs,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
default_value = "firtool",
|
||||||
|
env = "FIRTOOL",
|
||||||
|
value_hint = ValueHint::CommandName,
|
||||||
|
value_parser = OsStringValueParser::new().try_map(which)
|
||||||
|
)]
|
||||||
|
pub firtool: PathBuf,
|
||||||
|
#[arg(long)]
|
||||||
|
pub firtool_extra_args: Vec<OsString>,
|
||||||
|
/// adapt the generated Verilog for a particular toolchain
|
||||||
|
#[arg(long)]
|
||||||
|
pub verilog_dialect: Option<VerilogDialect>,
|
||||||
|
#[arg(long, short = 'g')]
|
||||||
|
pub debug: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct VerilogOutput {
|
||||||
|
pub firrtl: FirrtlOutput,
|
||||||
|
pub verilog_files: Vec<PathBuf>,
|
||||||
|
pub contents_hash: Option<blake3::Hash>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerilogOutput {
|
||||||
|
pub fn main_verilog_file(&self) -> PathBuf {
|
||||||
|
self.firrtl.file_with_ext("v")
|
||||||
|
}
|
||||||
|
fn unadjusted_verilog_file(&self) -> PathBuf {
|
||||||
|
self.firrtl.file_with_ext("unadjusted.v")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerilogArgs {
|
||||||
|
fn process_unadjusted_verilog_file(&self, mut output: VerilogOutput) -> Result<VerilogOutput> {
|
||||||
|
let input = fs::read_to_string(output.unadjusted_verilog_file())?;
|
||||||
|
let file_separator_prefix = "\n// ----- 8< ----- FILE \"";
|
||||||
|
let file_separator_suffix = "\" ----- 8< -----\n\n";
|
||||||
|
let mut input = &*input;
|
||||||
|
output.contents_hash = Some(blake3::hash(input.as_bytes()));
|
||||||
|
let main_verilog_file = output.main_verilog_file();
|
||||||
|
let mut file_name: Option<&Path> = Some(&main_verilog_file);
|
||||||
|
loop {
|
||||||
|
let (chunk, next_file_name) = if let Some((chunk, rest)) =
|
||||||
|
input.split_once(file_separator_prefix)
|
||||||
|
{
|
||||||
|
let Some((next_file_name, rest)) = rest.split_once(file_separator_suffix) else {
|
||||||
|
return Err(CliError(eyre!(
|
||||||
|
"parsing firtool's output failed: found {file_separator_prefix:?} but no {file_separator_suffix:?}"
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
input = rest;
|
||||||
|
(chunk, Some(next_file_name.as_ref()))
|
||||||
|
} else {
|
||||||
|
(mem::take(&mut input), None)
|
||||||
|
};
|
||||||
|
let Some(file_name) = mem::replace(&mut file_name, next_file_name) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let file_name = output.firrtl.output_dir.join(file_name);
|
||||||
|
fs::write(&file_name, chunk)?;
|
||||||
|
if let Some(extension) = file_name.extension() {
|
||||||
|
if extension == "v" || extension == "sv" {
|
||||||
|
output.verilog_files.push(file_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
fn run_impl(
|
||||||
|
&self,
|
||||||
|
firrtl_output: FirrtlOutput,
|
||||||
|
acquired_job: &mut AcquiredJob,
|
||||||
|
) -> Result<VerilogOutput> {
|
||||||
|
let Self {
|
||||||
|
firrtl,
|
||||||
|
firtool,
|
||||||
|
firtool_extra_args,
|
||||||
|
verilog_dialect,
|
||||||
|
debug,
|
||||||
|
} = self;
|
||||||
|
let output = VerilogOutput {
|
||||||
|
firrtl: firrtl_output,
|
||||||
|
verilog_files: vec![],
|
||||||
|
contents_hash: None,
|
||||||
|
};
|
||||||
|
let mut cmd = process::Command::new(firtool);
|
||||||
|
cmd.arg(output.firrtl.firrtl_file());
|
||||||
|
cmd.arg("-o");
|
||||||
|
cmd.arg(output.unadjusted_verilog_file());
|
||||||
|
if *debug {
|
||||||
|
cmd.arg("-g");
|
||||||
|
cmd.arg("--preserve-values=all");
|
||||||
|
}
|
||||||
|
if let Some(dialect) = verilog_dialect {
|
||||||
|
cmd.args(dialect.firtool_extra_args());
|
||||||
|
}
|
||||||
|
cmd.args(firtool_extra_args);
|
||||||
|
cmd.current_dir(&output.firrtl.output_dir);
|
||||||
|
let status = firrtl.base.run_external_command(acquired_job, cmd, None)?;
|
||||||
|
if status.success() {
|
||||||
|
self.process_unadjusted_verilog_file(output)
|
||||||
|
} else {
|
||||||
|
Err(CliError(eyre!(
|
||||||
|
"running {} failed: {status}",
|
||||||
|
self.firtool.display()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Arg> RunPhase<Arg> for VerilogArgs
|
||||||
|
where
|
||||||
|
FirrtlArgs: RunPhase<Arg, Output = FirrtlOutput>,
|
||||||
|
{
|
||||||
|
type Output = VerilogOutput;
|
||||||
|
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
|
||||||
|
let firrtl_output = self.firrtl.run_with_job(arg, acquired_job)?;
|
||||||
|
self.run_impl(firrtl_output, acquired_job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum FormalMode {
|
||||||
|
#[default]
|
||||||
|
BMC,
|
||||||
|
Prove,
|
||||||
|
Live,
|
||||||
|
Cover,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormalMode {
|
||||||
|
pub fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FormalMode::BMC => "bmc",
|
||||||
|
FormalMode::Prove => "prove",
|
||||||
|
FormalMode::Live => "live",
|
||||||
|
FormalMode::Cover => "cover",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FormalMode {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct FormalAdjustArgs;
|
||||||
|
|
||||||
|
impl clap::FromArgMatches for FormalAdjustArgs {
|
||||||
|
fn from_arg_matches(_matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl clap::Args for FormalAdjustArgs {
|
||||||
|
fn augment_args(cmd: clap::Command) -> clap::Command {
|
||||||
|
cmd.mut_arg("output", |arg| arg.required(false))
|
||||||
|
.mut_arg("verilog_dialect", |arg| {
|
||||||
|
arg.default_value(VerilogDialect::Yosys.to_string())
|
||||||
|
.hide(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
|
||||||
|
Self::augment_args(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn which(v: std::ffi::OsString) -> which::Result<PathBuf> {
|
||||||
|
#[cfg(not(miri))]
|
||||||
|
return which::which(v);
|
||||||
|
#[cfg(miri)]
|
||||||
|
return Ok(Path::new("/").join(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct FormalArgs {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub verilog: VerilogArgs,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
default_value = "sby",
|
||||||
|
env = "SBY",
|
||||||
|
value_hint = ValueHint::CommandName,
|
||||||
|
value_parser = OsStringValueParser::new().try_map(which)
|
||||||
|
)]
|
||||||
|
pub sby: PathBuf,
|
||||||
|
#[arg(long)]
|
||||||
|
pub sby_extra_args: Vec<String>,
|
||||||
|
#[arg(long, default_value_t)]
|
||||||
|
pub mode: FormalMode,
|
||||||
|
#[arg(long, default_value_t = Self::DEFAULT_DEPTH)]
|
||||||
|
pub depth: u64,
|
||||||
|
#[arg(long, default_value = "z3")]
|
||||||
|
pub solver: String,
|
||||||
|
#[arg(long)]
|
||||||
|
pub smtbmc_extra_args: Vec<String>,
|
||||||
|
#[arg(long, default_value_t = true, env = "FAYALITE_CACHE_RESULTS")]
|
||||||
|
pub cache_results: bool,
|
||||||
|
#[command(flatten)]
|
||||||
|
_formal_adjust_args: FormalAdjustArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for FormalArgs {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Self {
|
||||||
|
verilog,
|
||||||
|
sby,
|
||||||
|
sby_extra_args,
|
||||||
|
mode,
|
||||||
|
depth,
|
||||||
|
solver,
|
||||||
|
smtbmc_extra_args,
|
||||||
|
cache_results,
|
||||||
|
_formal_adjust_args: _,
|
||||||
|
} = self;
|
||||||
|
f.debug_struct("FormalArgs")
|
||||||
|
.field("verilog", verilog)
|
||||||
|
.field("sby", sby)
|
||||||
|
.field("sby_extra_args", sby_extra_args)
|
||||||
|
.field("mode", mode)
|
||||||
|
.field("depth", depth)
|
||||||
|
.field("solver", solver)
|
||||||
|
.field("smtbmc_extra_args", smtbmc_extra_args)
|
||||||
|
.field("cache_results", cache_results)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormalArgs {
|
||||||
|
pub const DEFAULT_DEPTH: u64 = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct FormalOutput {
|
||||||
|
pub verilog: VerilogOutput,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormalOutput {
|
||||||
|
pub fn sby_file(&self) -> PathBuf {
|
||||||
|
self.verilog.firrtl.file_with_ext("sby")
|
||||||
|
}
|
||||||
|
pub fn cache_file(&self) -> PathBuf {
|
||||||
|
self.verilog.firrtl.file_with_ext("cache.json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct FormalCacheOutput {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum FormalCacheVersion {
|
||||||
|
V1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormalCacheVersion {
|
||||||
|
pub const CURRENT: Self = Self::V1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct FormalCache {
|
||||||
|
pub version: FormalCacheVersion,
|
||||||
|
pub contents_hash: blake3::Hash,
|
||||||
|
pub stdout_stderr: String,
|
||||||
|
pub result: Result<FormalCacheOutput, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormalCache {
|
||||||
|
pub fn new(
|
||||||
|
version: FormalCacheVersion,
|
||||||
|
contents_hash: blake3::Hash,
|
||||||
|
stdout_stderr: String,
|
||||||
|
result: Result<FormalCacheOutput, String>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
version,
|
||||||
|
contents_hash,
|
||||||
|
stdout_stderr,
|
||||||
|
result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormalArgs {
|
||||||
|
fn sby_contents(&self, output: &FormalOutput) -> Result<String> {
|
||||||
|
let Self {
|
||||||
|
verilog: _,
|
||||||
|
sby: _,
|
||||||
|
sby_extra_args: _,
|
||||||
|
mode,
|
||||||
|
depth,
|
||||||
|
smtbmc_extra_args,
|
||||||
|
solver,
|
||||||
|
cache_results: _,
|
||||||
|
_formal_adjust_args: _,
|
||||||
|
} = self;
|
||||||
|
let smtbmc_options = smtbmc_extra_args.join(" ");
|
||||||
|
let top_module = &output.verilog.firrtl.top_module;
|
||||||
|
let mut retval = format!(
|
||||||
|
"[options]\n\
|
||||||
|
mode {mode}\n\
|
||||||
|
depth {depth}\n\
|
||||||
|
wait on\n\
|
||||||
|
\n\
|
||||||
|
[engines]\n\
|
||||||
|
smtbmc {solver} -- -- {smtbmc_options}\n\
|
||||||
|
\n\
|
||||||
|
[script]\n"
|
||||||
|
);
|
||||||
|
for verilog_file in &output.verilog.verilog_files {
|
||||||
|
let verilog_file = verilog_file
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| CliError(eyre!("verilog file path is not UTF-8")))?;
|
||||||
|
if verilog_file.contains(|ch: char| {
|
||||||
|
(ch != ' ' && ch != '\t' && ch.is_ascii_whitespace()) || ch == '"'
|
||||||
|
}) {
|
||||||
|
return Err(CliError(eyre!(
|
||||||
|
"verilog file path contains characters that aren't permitted"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
writeln!(retval, "read_verilog -sv -formal \"{verilog_file}\"").unwrap();
|
||||||
|
}
|
||||||
|
// workaround for wires disappearing -- set `keep` on all wires
|
||||||
|
writeln!(retval, "hierarchy -top {top_module}").unwrap();
|
||||||
|
writeln!(retval, "proc").unwrap();
|
||||||
|
writeln!(retval, "setattr -set keep 1 w:\\*").unwrap();
|
||||||
|
writeln!(retval, "prep").unwrap();
|
||||||
|
Ok(retval)
|
||||||
|
}
|
||||||
|
fn run_impl(
|
||||||
|
&self,
|
||||||
|
verilog_output: VerilogOutput,
|
||||||
|
acquired_job: &mut AcquiredJob,
|
||||||
|
) -> Result<FormalOutput> {
|
||||||
|
let output = FormalOutput {
|
||||||
|
verilog: verilog_output,
|
||||||
|
};
|
||||||
|
let sby_file = output.sby_file();
|
||||||
|
let sby_contents = self.sby_contents(&output)?;
|
||||||
|
let contents_hash = output.verilog.contents_hash.map(|verilog_hash| {
|
||||||
|
let mut hasher = blake3::Hasher::new();
|
||||||
|
hasher.update(verilog_hash.as_bytes());
|
||||||
|
hasher.update(sby_contents.as_bytes());
|
||||||
|
hasher.update(&(self.sby_extra_args.len() as u64).to_le_bytes());
|
||||||
|
for sby_extra_arg in self.sby_extra_args.iter() {
|
||||||
|
hasher.update(&(sby_extra_arg.len() as u64).to_le_bytes());
|
||||||
|
hasher.update(sby_extra_arg.as_bytes());
|
||||||
|
}
|
||||||
|
hasher.finalize()
|
||||||
|
});
|
||||||
|
std::fs::write(&sby_file, sby_contents)?;
|
||||||
|
let mut cmd = process::Command::new(&self.sby);
|
||||||
|
cmd.arg("-j1"); // sby seems not to respect job count in parallel mode
|
||||||
|
cmd.arg("-f");
|
||||||
|
cmd.arg(sby_file.file_name().unwrap());
|
||||||
|
cmd.args(&self.sby_extra_args);
|
||||||
|
cmd.current_dir(&output.verilog.firrtl.output_dir);
|
||||||
|
let mut captured_output = String::new();
|
||||||
|
let cache_file = output.cache_file();
|
||||||
|
let do_cache = if let Some(contents_hash) = contents_hash.filter(|_| self.cache_results) {
|
||||||
|
if let Some(FormalCache {
|
||||||
|
version: FormalCacheVersion::CURRENT,
|
||||||
|
contents_hash: cache_contents_hash,
|
||||||
|
stdout_stderr,
|
||||||
|
result,
|
||||||
|
}) = fs::read(&cache_file)
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| serde_json::from_slice(&v).ok())
|
||||||
|
{
|
||||||
|
if cache_contents_hash == contents_hash {
|
||||||
|
println!("Using cached formal result:\n{stdout_stderr}");
|
||||||
|
return match result {
|
||||||
|
Ok(FormalCacheOutput {}) => Ok(output),
|
||||||
|
Err(error) => Err(CliError(eyre::Report::msg(error))),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let _ = fs::remove_file(&cache_file);
|
||||||
|
let status = self.verilog.firrtl.base.run_external_command(
|
||||||
|
acquired_job,
|
||||||
|
cmd,
|
||||||
|
do_cache.then_some(&mut captured_output),
|
||||||
|
)?;
|
||||||
|
let result = if status.success() {
|
||||||
|
Ok(output)
|
||||||
|
} else {
|
||||||
|
Err(CliError(eyre!(
|
||||||
|
"running {} failed: {status}",
|
||||||
|
self.sby.display()
|
||||||
|
)))
|
||||||
|
};
|
||||||
|
fs::write(
|
||||||
|
cache_file,
|
||||||
|
serde_json::to_string_pretty(&FormalCache {
|
||||||
|
version: FormalCacheVersion::CURRENT,
|
||||||
|
contents_hash: contents_hash.unwrap(),
|
||||||
|
stdout_stderr: captured_output,
|
||||||
|
result: match &result {
|
||||||
|
Ok(FormalOutput { verilog: _ }) => Ok(FormalCacheOutput {}),
|
||||||
|
Err(error) => Err(error.to_string()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.expect("serialization shouldn't ever fail"),
|
||||||
|
)?;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Arg> RunPhase<Arg> for FormalArgs
|
||||||
|
where
|
||||||
|
VerilogArgs: RunPhase<Arg, Output = VerilogOutput>,
|
||||||
|
{
|
||||||
|
type Output = FormalOutput;
|
||||||
|
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
|
||||||
|
let verilog_output = self.verilog.run_with_job(arg, acquired_job)?;
|
||||||
|
self.run_impl(verilog_output, acquired_job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum CliCommand {
|
||||||
|
/// Generate FIRRTL
|
||||||
|
Firrtl(FirrtlArgs),
|
||||||
|
/// Generate Verilog
|
||||||
|
Verilog(VerilogArgs),
|
||||||
|
/// Run a formal proof
|
||||||
|
Formal(FormalArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a simple CLI
|
||||||
|
///
|
||||||
|
/// Use like:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use fayalite::prelude::*;
|
||||||
|
/// # #[hdl_module]
|
||||||
|
/// # fn my_module() {}
|
||||||
|
/// use fayalite::cli;
|
||||||
|
///
|
||||||
|
/// fn main() -> cli::Result {
|
||||||
|
/// cli::Cli::parse().run(my_module())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// You can also use it with a larger [`clap`]-based CLI like so:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use fayalite::prelude::*;
|
||||||
|
/// # #[hdl_module]
|
||||||
|
/// # fn my_module() {}
|
||||||
|
/// use clap::{Subcommand, Parser};
|
||||||
|
/// use fayalite::cli;
|
||||||
|
///
|
||||||
|
/// #[derive(Subcommand)]
|
||||||
|
/// pub enum Cmd {
|
||||||
|
/// #[command(flatten)]
|
||||||
|
/// Fayalite(cli::Cli),
|
||||||
|
/// MySpecialCommand {
|
||||||
|
/// #[arg(long)]
|
||||||
|
/// foo: bool,
|
||||||
|
/// },
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Parser)]
|
||||||
|
/// pub struct Cli {
|
||||||
|
/// #[command(subcommand)]
|
||||||
|
/// cmd: Cmd, // or just use cli::Cli directly if you don't need more subcommands
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() -> cli::Result {
|
||||||
|
/// match Cli::parse().cmd {
|
||||||
|
/// Cmd::Fayalite(v) => v.run(my_module())?,
|
||||||
|
/// Cmd::MySpecialCommand { foo } => println!("special: foo={foo}"),
|
||||||
|
/// }
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
// clear things that would be crate-specific
|
||||||
|
#[command(name = "Fayalite Simple CLI", about = None, long_about = None)]
|
||||||
|
pub struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
subcommand: CliCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl clap::Subcommand for Cli {
|
||||||
|
fn augment_subcommands(cmd: clap::Command) -> clap::Command {
|
||||||
|
CliCommand::augment_subcommands(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn augment_subcommands_for_update(cmd: clap::Command) -> clap::Command {
|
||||||
|
CliCommand::augment_subcommands_for_update(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_subcommand(name: &str) -> bool {
|
||||||
|
CliCommand::has_subcommand(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RunPhase<T> for Cli
|
||||||
|
where
|
||||||
|
FirrtlArgs: RunPhase<T, Output = FirrtlOutput>,
|
||||||
|
{
|
||||||
|
type Output = ();
|
||||||
|
fn run_with_job(&self, arg: T, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
|
||||||
|
match &self.subcommand {
|
||||||
|
CliCommand::Firrtl(c) => {
|
||||||
|
c.run_with_job(arg, acquired_job)?;
|
||||||
|
}
|
||||||
|
CliCommand::Verilog(c) => {
|
||||||
|
c.run_with_job(arg, acquired_job)?;
|
||||||
|
}
|
||||||
|
CliCommand::Formal(c) => {
|
||||||
|
c.run_with_job(arg, acquired_job)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cli {
|
||||||
|
/// forwards to [`clap::Parser::parse()`] so you don't have to import [`clap::Parser`]
|
||||||
|
pub fn parse() -> Self {
|
||||||
|
clap::Parser::parse()
|
||||||
|
}
|
||||||
|
/// forwards to [`RunPhase::run()`] so you don't have to import [`RunPhase`]
|
||||||
|
pub fn run<T>(&self, top_module: T) -> Result<()>
|
||||||
|
where
|
||||||
|
Self: RunPhase<T, Output = ()>,
|
||||||
|
{
|
||||||
|
RunPhase::run(self, top_module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,17 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{Expr, ValueType},
|
expr::{Expr, ToExpr},
|
||||||
hdl,
|
hdl,
|
||||||
int::Bool,
|
int::Bool,
|
||||||
reset::{Reset, ResetType},
|
reset::{Reset, ResetType},
|
||||||
sim::value::SimValue,
|
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{
|
ty::{
|
||||||
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
||||||
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties,
|
OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
||||||
impl_match_variant_as_self,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bitvec::{bits, order::Lsb0};
|
use bitvec::{bits, order::Lsb0};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||||
pub struct Clock;
|
pub struct Clock;
|
||||||
|
|
@ -72,15 +68,6 @@ impl Type for Clock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimValueDebug for Clock {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clock {
|
impl Clock {
|
||||||
pub fn type_properties(self) -> TypeProperties {
|
pub fn type_properties(self) -> TypeProperties {
|
||||||
Self::TYPE_PROPERTIES
|
Self::TYPE_PROPERTIES
|
||||||
|
|
@ -104,34 +91,29 @@ impl StaticType for Clock {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToClock {
|
pub trait ToClock {
|
||||||
type Output: ValueType<Type = Clock>;
|
fn to_clock(&self) -> Expr<Clock>;
|
||||||
fn to_clock(&self) -> Self::Output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + ToClock> ToClock for &'_ T {
|
impl<T: ?Sized + ToClock> ToClock for &'_ T {
|
||||||
type Output = T::Output;
|
fn to_clock(&self) -> Expr<Clock> {
|
||||||
fn to_clock(&self) -> Self::Output {
|
|
||||||
(**self).to_clock()
|
(**self).to_clock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + ToClock> ToClock for &'_ mut T {
|
impl<T: ?Sized + ToClock> ToClock for &'_ mut T {
|
||||||
type Output = T::Output;
|
fn to_clock(&self) -> Expr<Clock> {
|
||||||
fn to_clock(&self) -> Self::Output {
|
|
||||||
(**self).to_clock()
|
(**self).to_clock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + ToClock> ToClock for Box<T> {
|
impl<T: ?Sized + ToClock> ToClock for Box<T> {
|
||||||
type Output = T::Output;
|
fn to_clock(&self) -> Expr<Clock> {
|
||||||
fn to_clock(&self) -> Self::Output {
|
|
||||||
(**self).to_clock()
|
(**self).to_clock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToClock for Expr<Clock> {
|
impl ToClock for Expr<Clock> {
|
||||||
type Output = Expr<Clock>;
|
fn to_clock(&self) -> Expr<Clock> {
|
||||||
fn to_clock(&self) -> Self::Output {
|
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,25 +125,7 @@ pub struct ClockDomain<R: ResetType = Reset> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToClock for bool {
|
impl ToClock for bool {
|
||||||
type Output = SimValue<Clock>;
|
fn to_clock(&self) -> Expr<Clock> {
|
||||||
|
self.to_expr().to_clock()
|
||||||
fn to_clock(&self) -> Self::Output {
|
|
||||||
SimValue::from_value(Clock, *self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToClock for SimValue<Bool> {
|
|
||||||
type Output = SimValue<Clock>;
|
|
||||||
|
|
||||||
fn to_clock(&self) -> Self::Output {
|
|
||||||
SimValue::from_value(Clock, **self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToClock for SimValue<Clock> {
|
|
||||||
type Output = SimValue<Clock>;
|
|
||||||
|
|
||||||
fn to_clock(&self) -> Self::Output {
|
|
||||||
self.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{Expr, ToExpr, ValueType, ops::VariantAccess},
|
expr::{
|
||||||
|
Expr, ToExpr,
|
||||||
|
ops::{ExprPartialEq, VariantAccess},
|
||||||
|
},
|
||||||
hdl,
|
hdl,
|
||||||
int::{Bool, UIntValue},
|
int::{Bool, UIntValue},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
|
|
@ -10,12 +13,12 @@ use crate::{
|
||||||
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect,
|
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect,
|
||||||
enum_match_variants_helper, incomplete_wire, wire,
|
enum_match_variants_helper, incomplete_wire, wire,
|
||||||
},
|
},
|
||||||
sim::value::{SimValue, ToSimValue, ToSimValueWithType},
|
sim::value::{SimValue, SimValuePartialEq},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{
|
ty::{
|
||||||
CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize,
|
CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize,
|
||||||
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug,
|
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type,
|
||||||
StaticType, Type, TypeProperties,
|
TypeProperties,
|
||||||
},
|
},
|
||||||
util::HashMap,
|
util::HashMap,
|
||||||
};
|
};
|
||||||
|
|
@ -410,15 +413,6 @@ impl Type for Enum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimValueDebug for Enum {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
|
||||||
pub struct EnumPaddingSimValue {
|
pub struct EnumPaddingSimValue {
|
||||||
bits: Option<UIntValue>,
|
bits: Option<UIntValue>,
|
||||||
|
|
@ -605,7 +599,7 @@ impl<'a> EnumSimValueFromOpaque<'a> {
|
||||||
let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else {
|
let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else {
|
||||||
self.usage_error(true);
|
self.usage_error(true);
|
||||||
};
|
};
|
||||||
assert_eq!(value.ty(), T::from_canonical(variant_ty));
|
assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
|
||||||
SimValue::bits_mut(value)
|
SimValue::bits_mut(value)
|
||||||
.bits_mut()
|
.bits_mut()
|
||||||
.copy_from_bitslice(variant_bits);
|
.copy_from_bitslice(variant_bits);
|
||||||
|
|
@ -717,7 +711,7 @@ impl<'a> EnumSimValueToOpaque<'a> {
|
||||||
let Some(variant_ty) = self.variants[discriminant].ty else {
|
let Some(variant_ty) = self.variants[discriminant].ty else {
|
||||||
panic!("expected variant to have no field");
|
panic!("expected variant to have no field");
|
||||||
};
|
};
|
||||||
assert_eq!(value.ty(), T::from_canonical(variant_ty));
|
assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
|
||||||
self.known_variant(discriminant, Some(SimValue::opaque(value)), padding)
|
self.known_variant(discriminant, Some(SimValue::opaque(value)), padding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -732,12 +726,85 @@ pub fn enum_type_to_sim_builder<T: EnumType>(v: T) -> T::SimBuilder {
|
||||||
v.into()
|
v.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(cmp_eq)]
|
#[hdl]
|
||||||
pub enum HdlOption<T: Type> {
|
pub enum HdlOption<T: Type> {
|
||||||
HdlNone,
|
HdlNone,
|
||||||
HdlSome(T),
|
HdlSome(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
|
||||||
|
#[hdl]
|
||||||
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||||
|
#[hdl]
|
||||||
|
let cmp_eq = wire();
|
||||||
|
#[hdl]
|
||||||
|
match lhs {
|
||||||
|
HdlSome(lhs) =>
|
||||||
|
{
|
||||||
|
#[hdl]
|
||||||
|
match rhs {
|
||||||
|
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
|
||||||
|
HdlNone => connect(cmp_eq, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HdlNone =>
|
||||||
|
{
|
||||||
|
#[hdl]
|
||||||
|
match rhs {
|
||||||
|
HdlSome(_) => connect(cmp_eq, false),
|
||||||
|
HdlNone => connect(cmp_eq, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmp_eq
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||||
|
#[hdl]
|
||||||
|
let cmp_ne = wire();
|
||||||
|
#[hdl]
|
||||||
|
match lhs {
|
||||||
|
HdlSome(lhs) =>
|
||||||
|
{
|
||||||
|
#[hdl]
|
||||||
|
match rhs {
|
||||||
|
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
|
||||||
|
HdlNone => connect(cmp_ne, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HdlNone =>
|
||||||
|
{
|
||||||
|
#[hdl]
|
||||||
|
match rhs {
|
||||||
|
HdlSome(_) => connect(cmp_ne, true),
|
||||||
|
HdlNone => connect(cmp_ne, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmp_ne
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Lhs: SimValuePartialEq<Rhs>, Rhs: Type> SimValuePartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
|
||||||
|
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<HdlOption<Rhs>>) -> bool {
|
||||||
|
type SimValueMatch<T> = <T as Type>::SimValue;
|
||||||
|
match (&**this, &**other) {
|
||||||
|
(SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_)) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(SimValueMatch::<Self>::HdlSome(..), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_))
|
||||||
|
| (SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlSome(..)) => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
(
|
||||||
|
SimValueMatch::<Self>::HdlSome(l, _),
|
||||||
|
SimValueMatch::<HdlOption<Rhs>>::HdlSome(r, _),
|
||||||
|
) => l == r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
||||||
HdlOption[T::TYPE].HdlNone()
|
HdlOption[T::TYPE].HdlNone()
|
||||||
|
|
@ -746,124 +813,7 @@ pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
|
pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
|
||||||
let value = value.to_expr();
|
let value = value.to_expr();
|
||||||
HdlOption[value.ty()].HdlSome(value)
|
HdlOption[Expr::ty(value)].HdlSome(value)
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type> From<SimValue<HdlOption<T>>> for Option<SimValue<T>> {
|
|
||||||
#[hdl]
|
|
||||||
fn from(value: SimValue<HdlOption<T>>) -> Self {
|
|
||||||
#[hdl(sim)]
|
|
||||||
match value {
|
|
||||||
HdlSome(v) => Some(v),
|
|
||||||
HdlNone => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Type> From<&'a SimValue<HdlOption<T>>> for Option<&'a SimValue<T>> {
|
|
||||||
#[hdl]
|
|
||||||
fn from(value: &'a SimValue<HdlOption<T>>) -> Self {
|
|
||||||
#[hdl(sim)]
|
|
||||||
match value {
|
|
||||||
HdlSome(v) => Some(v),
|
|
||||||
HdlNone => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Type> From<&'a mut SimValue<HdlOption<T>>> for Option<&'a mut SimValue<T>> {
|
|
||||||
#[hdl]
|
|
||||||
fn from(value: &'a mut SimValue<HdlOption<T>>) -> Self {
|
|
||||||
#[hdl(sim)]
|
|
||||||
match value {
|
|
||||||
HdlSome(v) => Some(v),
|
|
||||||
HdlNone => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueType<Type: StaticType<MaskType: StaticType>>> ValueType for Option<T> {
|
|
||||||
type Type = HdlOption<T::Type>;
|
|
||||||
type ValueCategory = T::ValueCategory;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
StaticType::TYPE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, V: ToSimValueWithType<T>> ToSimValueWithType<HdlOption<T>> for Option<V> {
|
|
||||||
#[hdl]
|
|
||||||
fn to_sim_value_with_type(&self, ty: HdlOption<T>) -> SimValue<HdlOption<T>> {
|
|
||||||
match self {
|
|
||||||
Some(v) =>
|
|
||||||
{
|
|
||||||
#[hdl(sim)]
|
|
||||||
ty.HdlSome(v)
|
|
||||||
}
|
|
||||||
None =>
|
|
||||||
{
|
|
||||||
#[hdl(sim)]
|
|
||||||
ty.HdlNone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
fn into_sim_value_with_type(self, ty: HdlOption<T>) -> SimValue<HdlOption<T>> {
|
|
||||||
match self {
|
|
||||||
Some(v) =>
|
|
||||||
{
|
|
||||||
#[hdl(sim)]
|
|
||||||
ty.HdlSome(v)
|
|
||||||
}
|
|
||||||
None =>
|
|
||||||
{
|
|
||||||
#[hdl(sim)]
|
|
||||||
ty.HdlNone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ToSimValue<Type: StaticType<MaskType: StaticType>>> ToSimValue for Option<T> {
|
|
||||||
#[hdl]
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
|
||||||
match self {
|
|
||||||
Some(v) =>
|
|
||||||
{
|
|
||||||
#[hdl(sim)]
|
|
||||||
HdlSome(v)
|
|
||||||
}
|
|
||||||
None =>
|
|
||||||
{
|
|
||||||
#[hdl(sim)]
|
|
||||||
HdlNone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
fn into_sim_value(self) -> SimValue<Self::Type> {
|
|
||||||
match self {
|
|
||||||
Some(v) =>
|
|
||||||
{
|
|
||||||
#[hdl(sim)]
|
|
||||||
HdlSome(v)
|
|
||||||
}
|
|
||||||
None =>
|
|
||||||
{
|
|
||||||
#[hdl(sim)]
|
|
||||||
HdlNone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ToExpr<Type: StaticType<MaskType: StaticType>>> ToExpr for Option<T> {
|
|
||||||
fn to_expr(&self) -> Expr<Self::Type> {
|
|
||||||
match self {
|
|
||||||
Some(v) => HdlSome(v),
|
|
||||||
None => HdlNone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type> HdlOption<T> {
|
impl<T: Type> HdlOption<T> {
|
||||||
|
|
@ -903,7 +853,7 @@ impl<T: Type> HdlOption<T> {
|
||||||
let value = f(value).inspect_err(|_| {
|
let value = f(value).inspect_err(|_| {
|
||||||
and_then_out.complete(()); // avoid error
|
and_then_out.complete(()); // avoid error
|
||||||
})?;
|
})?;
|
||||||
let and_then_out = and_then_out.complete(value.ty());
|
let and_then_out = and_then_out.complete(Expr::ty(value));
|
||||||
connect(and_then_out, value);
|
connect(and_then_out, value);
|
||||||
drop(some_scope);
|
drop(some_scope);
|
||||||
let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) =
|
let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) =
|
||||||
|
|
@ -911,7 +861,7 @@ impl<T: Type> HdlOption<T> {
|
||||||
else {
|
else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
connect(and_then_out, and_then_out.ty().HdlNone());
|
connect(and_then_out, Expr::ty(and_then_out).HdlNone());
|
||||||
drop(none_scope);
|
drop(none_scope);
|
||||||
Ok(and_then_out)
|
Ok(and_then_out)
|
||||||
}
|
}
|
||||||
|
|
@ -929,8 +879,8 @@ impl<T: Type> HdlOption<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> {
|
pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let and_out = wire(opt_b.ty());
|
let and_out = wire(Expr::ty(opt_b));
|
||||||
connect(and_out, opt_b.ty().HdlNone());
|
connect(and_out, Expr::ty(opt_b).HdlNone());
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(_) = expr {
|
if let HdlSome(_) = expr {
|
||||||
connect(and_out, opt_b);
|
connect(and_out, opt_b);
|
||||||
|
|
@ -944,8 +894,8 @@ impl<T: Type> HdlOption<T> {
|
||||||
f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>,
|
f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>,
|
||||||
) -> Result<Expr<Self>, E> {
|
) -> Result<Expr<Self>, E> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let filtered = wire(expr.ty());
|
let filtered = wire(Expr::ty(expr));
|
||||||
connect(filtered, expr.ty().HdlNone());
|
connect(filtered, Expr::ty(expr).HdlNone());
|
||||||
let mut f = Some(f);
|
let mut f = Some(f);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(v) = expr {
|
if let HdlSome(v) = expr {
|
||||||
|
|
@ -1019,7 +969,7 @@ impl<T: Type> HdlOption<T> {
|
||||||
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
||||||
) -> Expr<R> {
|
) -> Expr<R> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let mapped = wire(default.ty());
|
let mapped = wire(Expr::ty(default));
|
||||||
let mut f = Some(f);
|
let mut f = Some(f);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
match expr {
|
match expr {
|
||||||
|
|
@ -1044,12 +994,12 @@ impl<T: Type> HdlOption<T> {
|
||||||
match expr {
|
match expr {
|
||||||
HdlSome(v) => {
|
HdlSome(v) => {
|
||||||
let v = f.take().unwrap()(v);
|
let v = f.take().unwrap()(v);
|
||||||
let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty()));
|
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
|
||||||
connect(mapped, v);
|
connect(mapped, v);
|
||||||
}
|
}
|
||||||
HdlNone => {
|
HdlNone => {
|
||||||
let v = default.take().unwrap()();
|
let v = default.take().unwrap()();
|
||||||
let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty()));
|
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
|
||||||
connect(mapped, v);
|
connect(mapped, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1059,7 +1009,7 @@ impl<T: Type> HdlOption<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
|
pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let or_out = wire(expr.ty());
|
let or_out = wire(Expr::ty(expr));
|
||||||
connect(or_out, opt_b);
|
connect(or_out, opt_b);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(_) = expr {
|
if let HdlSome(_) = expr {
|
||||||
|
|
@ -1071,7 +1021,7 @@ impl<T: Type> HdlOption<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> {
|
pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let or_else_out = wire(expr.ty());
|
let or_else_out = wire(Expr::ty(expr));
|
||||||
connect(or_else_out, f());
|
connect(or_else_out, f());
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(_) = expr {
|
if let HdlSome(_) = expr {
|
||||||
|
|
@ -1083,7 +1033,7 @@ impl<T: Type> HdlOption<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> {
|
pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let unwrap_or_else_out = wire(default.ty());
|
let unwrap_or_else_out = wire(Expr::ty(default));
|
||||||
connect(unwrap_or_else_out, default);
|
connect(unwrap_or_else_out, default);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(v) = expr {
|
if let HdlSome(v) = expr {
|
||||||
|
|
@ -1095,7 +1045,7 @@ impl<T: Type> HdlOption<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> {
|
pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let unwrap_or_else_out = wire(expr.ty().HdlSome);
|
let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome);
|
||||||
connect(unwrap_or_else_out, f());
|
connect(unwrap_or_else_out, f());
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(v) = expr {
|
if let HdlSome(v) = expr {
|
||||||
|
|
@ -1107,14 +1057,14 @@ impl<T: Type> HdlOption<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
|
pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let xor_out = wire(expr.ty());
|
let xor_out = wire(Expr::ty(expr));
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(_) = expr {
|
if let HdlSome(_) = expr {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlNone = opt_b {
|
if let HdlNone = opt_b {
|
||||||
connect(xor_out, expr);
|
connect(xor_out, expr);
|
||||||
} else {
|
} else {
|
||||||
connect(xor_out, expr.ty().HdlNone());
|
connect(xor_out, Expr::ty(expr).HdlNone());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
connect(xor_out, opt_b);
|
connect(xor_out, opt_b);
|
||||||
|
|
@ -1125,8 +1075,8 @@ impl<T: Type> HdlOption<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> {
|
pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let zip_out = wire(HdlOption[(expr.ty().HdlSome, other.ty().HdlSome)]);
|
let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]);
|
||||||
connect(zip_out, zip_out.ty().HdlNone());
|
connect(zip_out, Expr::ty(zip_out).HdlNone());
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(l) = expr {
|
if let HdlSome(l) = expr {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -1143,11 +1093,11 @@ impl<T: Type> HdlOption<HdlOption<T>> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> {
|
pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let flattened = wire(expr.ty().HdlSome);
|
let flattened = wire(Expr::ty(expr).HdlSome);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
match expr {
|
match expr {
|
||||||
HdlSome(v) => connect(flattened, v),
|
HdlSome(v) => connect(flattened, v),
|
||||||
HdlNone => connect(flattened, expr.ty().HdlSome.HdlNone()),
|
HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()),
|
||||||
}
|
}
|
||||||
flattened
|
flattened
|
||||||
}
|
}
|
||||||
|
|
@ -1157,7 +1107,7 @@ impl<T: Type, U: Type> HdlOption<(T, U)> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> {
|
pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> {
|
||||||
let (t, u) = expr.ty().HdlSome;
|
let (t, u) = Expr::ty(expr).HdlSome;
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let unzipped = wire((HdlOption[t], HdlOption[u]));
|
let unzipped = wire((HdlOption[t], HdlOption[u]));
|
||||||
connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone()));
|
connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone()));
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,222 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{expr::Valueless, expr::ops::make_impls, prelude::*};
|
|
||||||
use std::num::NonZero;
|
|
||||||
|
|
||||||
macro_rules! assert_neg_impls {
|
|
||||||
([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => {
|
|
||||||
const _: () = {
|
|
||||||
fn _check_neg_impl<$($Lifetimes)*$($Bounds)*>(v: $ty)
|
|
||||||
-> impl ValueType<
|
|
||||||
ValueCategory = <$ty as ValueType>::ValueCategory,
|
|
||||||
Type = <<Valueless<<$ty as ValueType>::Type> as std::ops::Neg>::Output as ValueType>::Type,
|
|
||||||
> {
|
|
||||||
std::ops::Neg::neg(v)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((sint_local<'a, Width>))]
|
|
||||||
assert_neg_impls! {}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! assert_not_impls {
|
|
||||||
([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => {
|
|
||||||
const _: () = {
|
|
||||||
fn _check_not_impl<$($Lifetimes)*$($Bounds)*>(v: $ty)
|
|
||||||
-> impl ValueType<
|
|
||||||
ValueCategory = <$ty as ValueType>::ValueCategory,
|
|
||||||
Type = <<Valueless<<$ty as ValueType>::Type> as std::ops::Not>::Output as ValueType>::Type,
|
|
||||||
> {
|
|
||||||
std::ops::Not::not(v)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((int_local<'a, Width>), (bool<'a>))]
|
|
||||||
assert_not_impls! {}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! assert_cast_to_bits_impls {
|
|
||||||
([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => {
|
|
||||||
const _: () = {
|
|
||||||
fn _check_cast_to_bits_impl<$($Lifetimes)*$($Bounds)*>(v: $ty)
|
|
||||||
-> impl ValueType<
|
|
||||||
ValueCategory = <$ty as ValueType>::ValueCategory,
|
|
||||||
Type = <<Valueless<<$ty as ValueType>::Type> as CastToBits>::Output as ValueType>::Type,
|
|
||||||
> {
|
|
||||||
<$ty as CastToBits>::cast_to_bits(&v)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((uint<'a, Width>), (sint<'a, Width>), (bool<'a>))]
|
|
||||||
assert_cast_to_bits_impls! {}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! assert_simple_bin_op_impls {
|
|
||||||
([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), !$Trait:ident::$f:ident) => {
|
|
||||||
#[expect(dead_code)]
|
|
||||||
const _: () = {
|
|
||||||
trait HasImpl {
|
|
||||||
fn check_impl(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
trait NoImpl {
|
|
||||||
fn check_impl(&self) -> &'static dyn NoImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L, R> NoImpl for (L, R) {
|
|
||||||
fn check_impl(&self) -> &'static dyn NoImpl {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L: std::ops::$Trait<R>, R> HasImpl for (L, R) {
|
|
||||||
fn check_impl(self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_simple_bin_op_no_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: $($R)*) {
|
|
||||||
let _: &'static dyn NoImpl = (l, r).check_impl();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] (usize), Shl::shl) => {
|
|
||||||
const _: () = {
|
|
||||||
#[expect(dead_code)]
|
|
||||||
fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: usize)
|
|
||||||
-> impl ValueType<
|
|
||||||
ValueCategory = <($($L)*, usize) as ValueType>::ValueCategory,
|
|
||||||
Type = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::Shl<usize>>::Output as ValueType>::Type,
|
|
||||||
> {
|
|
||||||
std::ops::Shl::shl(l, r)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] (usize), Shr::shr) => {
|
|
||||||
const _: () = {
|
|
||||||
#[expect(dead_code)]
|
|
||||||
fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: usize)
|
|
||||||
-> impl ValueType<
|
|
||||||
ValueCategory = <($($L)*, usize) as ValueType>::ValueCategory,
|
|
||||||
Type = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::Shr<usize>>::Output as ValueType>::Type,
|
|
||||||
> {
|
|
||||||
std::ops::Shr::shr(l, r)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), $Trait:ident::$f:ident) => {
|
|
||||||
const _: () = {
|
|
||||||
#[expect(dead_code)]
|
|
||||||
fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: $($R)*)
|
|
||||||
-> impl ValueType<
|
|
||||||
ValueCategory = <($($L)*, $($R)*) as ValueType>::ValueCategory,
|
|
||||||
Type = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::$Trait<Valueless<<$($R)* as ValueType>::Type>>>::Output as ValueType>::Type,
|
|
||||||
> {
|
|
||||||
std::ops::$Trait::$f(l, r)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
($LLifetimes:tt $LBounds:tt ($($L:tt)*), $RLifetimes:tt $RBounds:tt ($($R:tt)*), !$FirstTrait:ident::$first_f:ident, $($rest:tt)*) => {
|
|
||||||
assert_simple_bin_op_impls! {
|
|
||||||
$LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), !$FirstTrait::$first_f
|
|
||||||
}
|
|
||||||
assert_simple_bin_op_impls! {
|
|
||||||
$LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $($rest)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($LLifetimes:tt $LBounds:tt ($($L:tt)*), $RLifetimes:tt $RBounds:tt ($($R:tt)*), $FirstTrait:ident::$first_f:ident, $($rest:tt)*) => {
|
|
||||||
assert_simple_bin_op_impls! {
|
|
||||||
$LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $FirstTrait::$first_f
|
|
||||||
}
|
|
||||||
assert_simple_bin_op_impls! {
|
|
||||||
$LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $($rest)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((uint_local<'l, L>))]
|
|
||||||
#[kinds((uint<'r, R>))]
|
|
||||||
assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((uint<'l, L>))]
|
|
||||||
#[kinds((uint_local<'r, R>))]
|
|
||||||
assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor, Shl::shl, Shr::shr}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((sint_local<'l, L>))]
|
|
||||||
#[kinds((sint<'r, R>))]
|
|
||||||
assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((sint<'l, L>))]
|
|
||||||
#[kinds((sint_local<'r, R>))]
|
|
||||||
assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((sint<'l, L>))]
|
|
||||||
#[kinds((uint_local<'r, R>))]
|
|
||||||
assert_simple_bin_op_impls! {Shl::shl, Shr::shr}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((uint_local<'l, L>))]
|
|
||||||
assert_simple_bin_op_impls! {[][] (usize), Shl::shl, Shr::shr}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((sint_local<'l, L>))]
|
|
||||||
assert_simple_bin_op_impls! {[][] (usize), Shl::shl, Shr::shr}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((bool_local<'l>))]
|
|
||||||
#[kinds((bool_local<'r>))]
|
|
||||||
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((bool<'l>))]
|
|
||||||
#[kinds((Valueless<Bool>))]
|
|
||||||
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((Valueless<Bool>))]
|
|
||||||
#[kinds((bool<'r>))]
|
|
||||||
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((bool_at_most_sim_value<'l>))]
|
|
||||||
#[kinds((bool_at_most_sim_value<'r>))]
|
|
||||||
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert there are no impls of BitAnd/BitOr/BitXor between Expr<Bool> and Rust's bool,
|
|
||||||
// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do
|
|
||||||
// what is usually wanted.
|
|
||||||
make_impls! {
|
|
||||||
#[kinds((Expr<Bool>))]
|
|
||||||
#[kinds(bool)]
|
|
||||||
assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_impls! {
|
|
||||||
#[kinds(bool)]
|
|
||||||
#[kinds((Expr<Bool>))]
|
|
||||||
assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor}
|
|
||||||
}
|
|
||||||
|
|
@ -3,15 +3,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
array::Array,
|
array::Array,
|
||||||
bundle::{Bundle, BundleField},
|
bundle::{Bundle, BundleField},
|
||||||
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr},
|
expr::{Expr, Flow, ToExpr},
|
||||||
formal::FormalInput,
|
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
memory::{DynPortType, MemPort},
|
memory::{DynPortType, MemPort},
|
||||||
module::{Instance, ModuleIO, TargetName},
|
module::{Instance, ModuleIO, TargetName},
|
||||||
reg::Reg,
|
reg::Reg,
|
||||||
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
|
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{CanonicalType, TraceAsString, Type},
|
ty::{CanonicalType, Type},
|
||||||
wire::Wire,
|
wire::Wire,
|
||||||
};
|
};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
@ -47,33 +46,11 @@ impl fmt::Display for TargetPathDynArrayElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct TargetPathTraceAsStringInner {}
|
|
||||||
|
|
||||||
impl fmt::Display for TargetPathTraceAsStringInner {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, ".<inner>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct TargetPathToTraceAsString {
|
|
||||||
pub ty: TraceAsString<CanonicalType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for TargetPathToTraceAsString {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, ".to_trace_as_string()")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum TargetPathElement {
|
pub enum TargetPathElement {
|
||||||
BundleField(TargetPathBundleField),
|
BundleField(TargetPathBundleField),
|
||||||
ArrayElement(TargetPathArrayElement),
|
ArrayElement(TargetPathArrayElement),
|
||||||
DynArrayElement(TargetPathDynArrayElement),
|
DynArrayElement(TargetPathDynArrayElement),
|
||||||
TraceAsStringInner(TargetPathTraceAsStringInner),
|
|
||||||
ToTraceAsString(TargetPathToTraceAsString),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TargetPathBundleField> for TargetPathElement {
|
impl From<TargetPathBundleField> for TargetPathElement {
|
||||||
|
|
@ -94,26 +71,12 @@ impl From<TargetPathDynArrayElement> for TargetPathElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TargetPathTraceAsStringInner> for TargetPathElement {
|
|
||||||
fn from(value: TargetPathTraceAsStringInner) -> Self {
|
|
||||||
Self::TraceAsStringInner(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TargetPathToTraceAsString> for TargetPathElement {
|
|
||||||
fn from(value: TargetPathToTraceAsString) -> Self {
|
|
||||||
Self::ToTraceAsString(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for TargetPathElement {
|
impl fmt::Display for TargetPathElement {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::BundleField(v) => v.fmt(f),
|
Self::BundleField(v) => v.fmt(f),
|
||||||
Self::ArrayElement(v) => v.fmt(f),
|
Self::ArrayElement(v) => v.fmt(f),
|
||||||
Self::DynArrayElement(v) => v.fmt(f),
|
Self::DynArrayElement(v) => v.fmt(f),
|
||||||
Self::TraceAsStringInner(v) => v.fmt(f),
|
|
||||||
Self::ToTraceAsString(v) => v.fmt(f),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -137,15 +100,6 @@ impl TargetPathElement {
|
||||||
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
|
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
|
||||||
parent_ty.element()
|
parent_ty.element()
|
||||||
}
|
}
|
||||||
Self::TraceAsStringInner(_) => {
|
|
||||||
let parent_ty =
|
|
||||||
TraceAsString::<CanonicalType>::from_canonical(parent.canonical_ty());
|
|
||||||
parent_ty.inner_ty()
|
|
||||||
}
|
|
||||||
&Self::ToTraceAsString(TargetPathToTraceAsString { ty }) => {
|
|
||||||
assert_eq!(parent.canonical_ty(), ty.inner_ty());
|
|
||||||
ty.canonical()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn flow(&self, parent: Interned<Target>) -> Flow {
|
pub fn flow(&self, parent: Interned<Target>) -> Flow {
|
||||||
|
|
@ -157,18 +111,13 @@ impl TargetPathElement {
|
||||||
.expect("field name is known to be a valid field of parent type");
|
.expect("field name is known to be a valid field of parent type");
|
||||||
parent.flow().flip_if(field.flipped)
|
parent.flow().flip_if(field.flipped)
|
||||||
}
|
}
|
||||||
Self::ArrayElement(_)
|
Self::ArrayElement(_) => parent.flow(),
|
||||||
| Self::DynArrayElement(_)
|
Self::DynArrayElement(_) => parent.flow(),
|
||||||
| Self::TraceAsStringInner(_)
|
|
||||||
| Self::ToTraceAsString(_) => parent.flow(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_static(&self) -> bool {
|
pub fn is_static(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::BundleField(_)
|
Self::BundleField(_) | Self::ArrayElement(_) => true,
|
||||||
| Self::ArrayElement(_)
|
|
||||||
| Self::TraceAsStringInner(_)
|
|
||||||
| Self::ToTraceAsString(_) => true,
|
|
||||||
Self::DynArrayElement(_) => false,
|
Self::DynArrayElement(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -247,18 +196,9 @@ macro_rules! impl_target_base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueType for $TargetBase {
|
|
||||||
type Type = CanonicalType;
|
|
||||||
type ValueCategory = ValueCategoryExpr;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
match self {
|
|
||||||
$(Self::$Variant(v) => v.ty().canonical(),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToExpr for $TargetBase {
|
impl ToExpr for $TargetBase {
|
||||||
|
type Type = CanonicalType;
|
||||||
|
|
||||||
fn to_expr(&self) -> Expr<Self::Type> {
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
match self {
|
match self {
|
||||||
$(Self::$Variant(v) => Expr::canonical(v.to_expr()),)*
|
$(Self::$Variant(v) => Expr::canonical(v.to_expr()),)*
|
||||||
|
|
@ -296,14 +236,6 @@ impl_target_base! {
|
||||||
#[is = is_instance]
|
#[is = is_instance]
|
||||||
#[to = instance]
|
#[to = instance]
|
||||||
Instance(Instance<Bundle>),
|
Instance(Instance<Bundle>),
|
||||||
#[from = from]
|
|
||||||
#[is = is_formal_input]
|
|
||||||
#[to = formal_input]
|
|
||||||
FormalInput(FormalInput),
|
|
||||||
#[from = from]
|
|
||||||
#[is = is_sim_io_for_global]
|
|
||||||
#[to = sim_io_for_global]
|
|
||||||
SimIoForGlobal(crate::expr::ops::SimIoForGlobal),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,8 +284,6 @@ impl TargetBase {
|
||||||
TargetBase::RegAsync(v) => TargetName(v.scoped_name(), None),
|
TargetBase::RegAsync(v) => TargetName(v.scoped_name(), None),
|
||||||
TargetBase::Wire(v) => TargetName(v.scoped_name(), None),
|
TargetBase::Wire(v) => TargetName(v.scoped_name(), None),
|
||||||
TargetBase::Instance(v) => TargetName(v.scoped_name(), None),
|
TargetBase::Instance(v) => TargetName(v.scoped_name(), None),
|
||||||
TargetBase::FormalInput(v) => TargetName(v.scoped_name(), None),
|
|
||||||
TargetBase::SimIoForGlobal(v) => TargetName(v.global().scoped_name(), None),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn canonical_ty(&self) -> CanonicalType {
|
pub fn canonical_ty(&self) -> CanonicalType {
|
||||||
|
|
@ -365,21 +295,6 @@ impl TargetBase {
|
||||||
TargetBase::RegAsync(v) => v.ty(),
|
TargetBase::RegAsync(v) => v.ty(),
|
||||||
TargetBase::Wire(v) => v.ty(),
|
TargetBase::Wire(v) => v.ty(),
|
||||||
TargetBase::Instance(v) => v.ty().canonical(),
|
TargetBase::Instance(v) => v.ty().canonical(),
|
||||||
TargetBase::FormalInput(v) => v.ty(),
|
|
||||||
TargetBase::SimIoForGlobal(v) => v.ty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn is_valid_annotation_target(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::ModuleIO(_) => true,
|
|
||||||
Self::MemPort(_) => true,
|
|
||||||
Self::Reg(_) => true,
|
|
||||||
Self::RegSync(_) => true,
|
|
||||||
Self::RegAsync(_) => true,
|
|
||||||
Self::Wire(_) => true,
|
|
||||||
Self::Instance(_) => true,
|
|
||||||
Self::FormalInput(_) => false,
|
|
||||||
Self::SimIoForGlobal(_) => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -390,7 +305,6 @@ pub struct TargetChild {
|
||||||
path_element: Interned<TargetPathElement>,
|
path_element: Interned<TargetPathElement>,
|
||||||
canonical_ty: CanonicalType,
|
canonical_ty: CanonicalType,
|
||||||
flow: Flow,
|
flow: Flow,
|
||||||
canonicalized_if_different: Option<Interned<Target>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for TargetChild {
|
impl fmt::Debug for TargetChild {
|
||||||
|
|
@ -400,7 +314,6 @@ impl fmt::Debug for TargetChild {
|
||||||
path_element,
|
path_element,
|
||||||
canonical_ty: _,
|
canonical_ty: _,
|
||||||
flow: _,
|
flow: _,
|
||||||
canonicalized_if_different: _,
|
|
||||||
} = self;
|
} = self;
|
||||||
parent.fmt(f)?;
|
parent.fmt(f)?;
|
||||||
fmt::Display::fmt(path_element, f)
|
fmt::Display::fmt(path_element, f)
|
||||||
|
|
@ -414,7 +327,6 @@ impl fmt::Display for TargetChild {
|
||||||
path_element,
|
path_element,
|
||||||
canonical_ty: _,
|
canonical_ty: _,
|
||||||
flow: _,
|
flow: _,
|
||||||
canonicalized_if_different: _,
|
|
||||||
} = self;
|
} = self;
|
||||||
parent.fmt(f)?;
|
parent.fmt(f)?;
|
||||||
path_element.fmt(f)
|
path_element.fmt(f)
|
||||||
|
|
@ -422,69 +334,14 @@ impl fmt::Display for TargetChild {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TargetChild {
|
impl TargetChild {
|
||||||
fn new_helper(
|
pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self {
|
||||||
parent: Interned<Target>,
|
|
||||||
path_element: Interned<TargetPathElement>,
|
|
||||||
canonicalized_if_different: Option<Interned<Target>>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
parent,
|
parent,
|
||||||
path_element,
|
path_element,
|
||||||
canonical_ty: path_element.canonical_ty(parent),
|
canonical_ty: path_element.canonical_ty(parent),
|
||||||
flow: path_element.flow(parent),
|
flow: path_element.flow(parent),
|
||||||
canonicalized_if_different,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn make_canonicalized_if_different(
|
|
||||||
parent: Interned<Target>,
|
|
||||||
path_element: Interned<TargetPathElement>,
|
|
||||||
) -> Option<Interned<Target>> {
|
|
||||||
use TargetPathElement::*;
|
|
||||||
match *path_element {
|
|
||||||
BundleField(_) => {}
|
|
||||||
ArrayElement(_) => {}
|
|
||||||
DynArrayElement(_) => {}
|
|
||||||
TraceAsStringInner(_) => {
|
|
||||||
if let Some(child) = parent.canonicalized().child() {
|
|
||||||
match *child.path_element() {
|
|
||||||
BundleField(_)
|
|
||||||
| ArrayElement(_)
|
|
||||||
| DynArrayElement(_)
|
|
||||||
| TraceAsStringInner(_) => {}
|
|
||||||
ToTraceAsString(_) => return Some(child.parent()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ToTraceAsString(TargetPathToTraceAsString { ty }) => {
|
|
||||||
if let Some(child) = parent.canonicalized().child() {
|
|
||||||
match *child.path_element() {
|
|
||||||
BundleField(_) | ArrayElement(_) | DynArrayElement(_)
|
|
||||||
| ToTraceAsString(_) => {}
|
|
||||||
TraceAsStringInner(_) => {
|
|
||||||
if ty.canonical() == child.parent().canonical_ty() {
|
|
||||||
return Some(child.parent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(
|
|
||||||
Target::Child(Self::new_helper(
|
|
||||||
parent.canonicalized_if_different()?,
|
|
||||||
path_element,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.intern_sized(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self {
|
|
||||||
Self::new_helper(
|
|
||||||
parent,
|
|
||||||
path_element,
|
|
||||||
Self::make_canonicalized_if_different(parent, path_element),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn parent(self) -> Interned<Target> {
|
pub fn parent(self) -> Interned<Target> {
|
||||||
self.parent
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
@ -497,19 +354,6 @@ impl TargetChild {
|
||||||
pub fn flow(self) -> Flow {
|
pub fn flow(self) -> Flow {
|
||||||
self.flow
|
self.flow
|
||||||
}
|
}
|
||||||
pub fn is_canonicalized(self) -> bool {
|
|
||||||
self.canonicalized_if_different.is_none()
|
|
||||||
}
|
|
||||||
pub fn canonicalized_if_different(self) -> Option<Interned<Target>> {
|
|
||||||
self.canonicalized_if_different
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub fn canonicalized(self) -> Target {
|
|
||||||
match self.canonicalized_if_different {
|
|
||||||
Some(v) => *v,
|
|
||||||
None => Target::Child(self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn bundle_field(self) -> Option<BundleField> {
|
pub fn bundle_field(self) -> Option<BundleField> {
|
||||||
if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element {
|
if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element {
|
||||||
let parent_ty = Bundle::from_canonical(self.parent.canonical_ty());
|
let parent_ty = Bundle::from_canonical(self.parent.canonical_ty());
|
||||||
|
|
@ -574,16 +418,6 @@ impl Target {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_valid_annotation_target(&self) -> bool {
|
|
||||||
let mut target = self;
|
|
||||||
loop {
|
|
||||||
match target {
|
|
||||||
Self::Base(target_base) => return target_base.is_valid_annotation_target(),
|
|
||||||
Self::Child(v) if !v.path_element().is_static() => return false,
|
|
||||||
Self::Child(v) => target = &v.parent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self {
|
pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self {
|
||||||
TargetChild::new(self.intern(), path_element).into()
|
TargetChild::new(self.intern(), path_element).into()
|
||||||
|
|
@ -600,82 +434,6 @@ impl Target {
|
||||||
Target::Child(v) => v.canonical_ty(),
|
Target::Child(v) => v.canonical_ty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_canonicalized(self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Base(_) => true,
|
|
||||||
Self::Child(child) => child.is_canonicalized(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn canonicalized_if_different(self) -> Option<Interned<Self>> {
|
|
||||||
match self {
|
|
||||||
Self::Base(_) => None,
|
|
||||||
Self::Child(child) => child.canonicalized_if_different(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub fn canonicalized(self) -> Target {
|
|
||||||
match self.canonicalized_if_different() {
|
|
||||||
Some(v) => *v,
|
|
||||||
None => self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub fn canonicalized_interned(this: Interned<Target>) -> Interned<Target> {
|
|
||||||
this.canonicalized_if_different().unwrap_or(this)
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap_transparent_types(mut self) -> Target {
|
|
||||||
loop {
|
|
||||||
self = self.canonicalized();
|
|
||||||
match self.canonical_ty() {
|
|
||||||
CanonicalType::UInt(_)
|
|
||||||
| CanonicalType::SInt(_)
|
|
||||||
| CanonicalType::Bool(_)
|
|
||||||
| CanonicalType::Array(_)
|
|
||||||
| CanonicalType::Enum(_)
|
|
||||||
| CanonicalType::Bundle(_)
|
|
||||||
| CanonicalType::AsyncReset(_)
|
|
||||||
| CanonicalType::SyncReset(_)
|
|
||||||
| CanonicalType::Reset(_)
|
|
||||||
| CanonicalType::Clock(_)
|
|
||||||
| CanonicalType::PhantomConst(_)
|
|
||||||
| CanonicalType::DynSimOnly(_) => return self,
|
|
||||||
CanonicalType::TraceAsString(_) => {
|
|
||||||
if let Self::Child(child) = self
|
|
||||||
&& let TargetPathElement::ToTraceAsString(_) = *child.path_element()
|
|
||||||
{
|
|
||||||
self = *child.parent();
|
|
||||||
} else {
|
|
||||||
self = self.join(TargetPathElement::intern_sized(
|
|
||||||
TargetPathTraceAsStringInner {}.into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap_transparent_types_interned(this: Interned<Target>) -> Interned<Target> {
|
|
||||||
let retval = this.unwrap_transparent_types();
|
|
||||||
if retval != *this {
|
|
||||||
retval.intern_sized()
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub fn without_trailing_transparent_path_elements(mut self) -> Target {
|
|
||||||
use TargetPathElement::*;
|
|
||||||
loop {
|
|
||||||
match self {
|
|
||||||
Self::Base(_) => return self,
|
|
||||||
Self::Child(child) => match *child.path_element() {
|
|
||||||
BundleField(_) | ArrayElement(_) | DynArrayElement(_) => return self,
|
|
||||||
TraceAsStringInner(_) | ToTraceAsString(_) => self = *child.parent(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Target {
|
impl fmt::Display for Target {
|
||||||
|
|
@ -700,18 +458,6 @@ pub trait GetTarget {
|
||||||
fn target(&self) -> Option<Interned<Target>>;
|
fn target(&self) -> Option<Interned<Target>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetTarget for Target {
|
|
||||||
fn target(&self) -> Option<Interned<Target>> {
|
|
||||||
Some(self.intern())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetTarget for TargetBase {
|
|
||||||
fn target(&self) -> Option<Interned<Target>> {
|
|
||||||
Some(Target::Base(self.intern()).intern_sized())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetTarget for bool {
|
impl GetTarget for bool {
|
||||||
fn target(&self) -> Option<Interned<Target>> {
|
fn target(&self) -> Option<Interned<Target>> {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -1,378 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{expr::ValueType, ty::Type};
|
|
||||||
|
|
||||||
trait ValueCategorySealed {}
|
|
||||||
|
|
||||||
#[expect(private_bounds)]
|
|
||||||
pub trait ValueCategory:
|
|
||||||
ValueCategorySealed
|
|
||||||
+ Copy
|
|
||||||
+ Ord
|
|
||||||
+ Default
|
|
||||||
+ std::fmt::Debug
|
|
||||||
+ std::hash::Hash
|
|
||||||
+ 'static
|
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
{
|
|
||||||
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>>: ValueType<Type = D::Type, ValueCategory: ValueCategoryIsValueSimValueExprOrValueless>;
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> Self::DispatchOutput<D>;
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> ValueCategoryDispatchOutputEnum<D>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
|
|
||||||
pub struct ValueCategoryValue;
|
|
||||||
|
|
||||||
impl ValueCategorySealed for ValueCategoryValue {}
|
|
||||||
|
|
||||||
impl ValueCategory for ValueCategoryValue {
|
|
||||||
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::Value;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> Self::DispatchOutput<D> {
|
|
||||||
dispatch.dispatch_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> ValueCategoryDispatchOutputEnum<D> {
|
|
||||||
ValueCategoryDispatchOutputEnum::Value(dispatch.dispatch_value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
|
|
||||||
pub struct ValueCategorySimValue;
|
|
||||||
|
|
||||||
impl ValueCategorySealed for ValueCategorySimValue {}
|
|
||||||
|
|
||||||
impl ValueCategory for ValueCategorySimValue {
|
|
||||||
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::SimValue;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> Self::DispatchOutput<D> {
|
|
||||||
dispatch.dispatch_sim_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> ValueCategoryDispatchOutputEnum<D> {
|
|
||||||
ValueCategoryDispatchOutputEnum::SimValue(dispatch.dispatch_sim_value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
|
|
||||||
pub struct ValueCategoryExpr;
|
|
||||||
|
|
||||||
impl ValueCategorySealed for ValueCategoryExpr {}
|
|
||||||
|
|
||||||
impl ValueCategory for ValueCategoryExpr {
|
|
||||||
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::Expr;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> Self::DispatchOutput<D> {
|
|
||||||
dispatch.dispatch_expr()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> ValueCategoryDispatchOutputEnum<D> {
|
|
||||||
ValueCategoryDispatchOutputEnum::Expr(dispatch.dispatch_expr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
|
|
||||||
pub struct ValueCategoryValueless;
|
|
||||||
|
|
||||||
impl ValueCategorySealed for ValueCategoryValueless {}
|
|
||||||
|
|
||||||
impl ValueCategory for ValueCategoryValueless {
|
|
||||||
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::Valueless;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> Self::DispatchOutput<D> {
|
|
||||||
dispatch.dispatch_valueless()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
|
|
||||||
dispatch: D,
|
|
||||||
) -> ValueCategoryDispatchOutputEnum<D> {
|
|
||||||
ValueCategoryDispatchOutputEnum::Valueless(dispatch.dispatch_valueless())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ValueCategoryIsValue: ValueCategoryIsValueOrSimValue {}
|
|
||||||
|
|
||||||
impl ValueCategoryIsValue for ValueCategoryValue {}
|
|
||||||
|
|
||||||
pub trait ValueCategoryIsValueOrSimValue: ValueCategoryIsValueSimValueOrExpr {}
|
|
||||||
|
|
||||||
impl ValueCategoryIsValueOrSimValue for ValueCategoryValue {}
|
|
||||||
impl ValueCategoryIsValueOrSimValue for ValueCategorySimValue {}
|
|
||||||
|
|
||||||
pub trait ValueCategoryIsValueSimValueOrExpr: ValueCategoryIsValueSimValueExprOrValueless {}
|
|
||||||
|
|
||||||
impl ValueCategoryIsValueSimValueOrExpr for ValueCategoryValue {}
|
|
||||||
impl ValueCategoryIsValueSimValueOrExpr for ValueCategorySimValue {}
|
|
||||||
impl ValueCategoryIsValueSimValueOrExpr for ValueCategoryExpr {}
|
|
||||||
|
|
||||||
pub trait ValueCategoryIsValueSimValueExprOrValueless: ValueCategory {}
|
|
||||||
|
|
||||||
impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryValue {}
|
|
||||||
impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategorySimValue {}
|
|
||||||
impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryExpr {}
|
|
||||||
impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryValueless {}
|
|
||||||
|
|
||||||
pub trait ValueCategoryIsValueless: ValueCategoryIsExprOrValueless {}
|
|
||||||
|
|
||||||
impl ValueCategoryIsValueless for ValueCategoryValueless {}
|
|
||||||
|
|
||||||
pub trait ValueCategoryIsExprOrValueless: ValueCategoryIsValueSimValueExprOrValueless {}
|
|
||||||
|
|
||||||
impl ValueCategoryIsExprOrValueless for ValueCategoryExpr {}
|
|
||||||
impl ValueCategoryIsExprOrValueless for ValueCategoryValueless {}
|
|
||||||
|
|
||||||
pub trait ValueCategoryIsSimValueExprOrValueless:
|
|
||||||
ValueCategoryIsValueSimValueExprOrValueless
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValueCategoryIsSimValueExprOrValueless for ValueCategorySimValue {}
|
|
||||||
impl ValueCategoryIsSimValueExprOrValueless for ValueCategoryExpr {}
|
|
||||||
impl ValueCategoryIsSimValueExprOrValueless for ValueCategoryValueless {}
|
|
||||||
|
|
||||||
trait ValueCategoryCommonSealed {}
|
|
||||||
|
|
||||||
#[expect(private_bounds)]
|
|
||||||
pub trait ValueCategoryCommon<
|
|
||||||
T: ValueCategoryCommonSealed
|
|
||||||
+ Copy
|
|
||||||
+ Ord
|
|
||||||
+ Default
|
|
||||||
+ std::fmt::Debug
|
|
||||||
+ std::hash::Hash
|
|
||||||
+ 'static
|
|
||||||
+ Send
|
|
||||||
+ Sync,
|
|
||||||
>: ValueCategory
|
|
||||||
{
|
|
||||||
type Common: ValueCategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_value_category_common {
|
|
||||||
($($T:ident),* $(,)?) => {
|
|
||||||
impl_value_category_common!([$($T,)*]);
|
|
||||||
};
|
|
||||||
([$($A:ident,)?]) => {};
|
|
||||||
([$FirstT:ident, $($T:ident,)+]) => {
|
|
||||||
impl_value_category_common!([$($T,)*]);
|
|
||||||
|
|
||||||
impl<$FirstT: ValueCategory, $($T: ValueCategory),*> ValueCategoryCommonSealed for ($FirstT, $($T),*) {}
|
|
||||||
|
|
||||||
impl<This, $FirstT, $($T),*> ValueCategoryCommon<($FirstT, $($T),*)> for This
|
|
||||||
where
|
|
||||||
$FirstT: ValueCategory,
|
|
||||||
$($T: ValueCategory,)*
|
|
||||||
This: ValueCategoryCommon<($FirstT,), Common: ValueCategoryCommon<($($T,)*)>>,
|
|
||||||
{
|
|
||||||
type Common = <<This as ValueCategoryCommon<($FirstT,)>>::Common as ValueCategoryCommon<($($T,)*)>>::Common;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_value_category_common!(T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0);
|
|
||||||
|
|
||||||
impl<T: ValueCategory> ValueCategoryCommonSealed for (T,) {}
|
|
||||||
|
|
||||||
impl ValueCategoryCommonSealed for () {}
|
|
||||||
|
|
||||||
impl<T: ValueCategory> ValueCategoryCommon<()> for T {
|
|
||||||
type Common = T;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueCategoryIsValue> ValueCategoryCommon<(ValueCategoryValue,)> for T {
|
|
||||||
type Common = ValueCategoryValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueCategoryIsValueOrSimValue> ValueCategoryCommon<(ValueCategorySimValue,)> for T {
|
|
||||||
type Common = ValueCategorySimValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueCategoryIsValue> ValueCategoryCommon<(T,)> for ValueCategorySimValue {
|
|
||||||
type Common = ValueCategorySimValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueCategoryIsValueSimValueOrExpr> ValueCategoryCommon<(ValueCategoryExpr,)> for T {
|
|
||||||
type Common = ValueCategoryExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueCategoryIsValueOrSimValue> ValueCategoryCommon<(T,)> for ValueCategoryExpr {
|
|
||||||
type Common = ValueCategoryExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueCategoryIsValueSimValueExprOrValueless> ValueCategoryCommon<(ValueCategoryValueless,)>
|
|
||||||
for T
|
|
||||||
{
|
|
||||||
type Common = ValueCategoryValueless;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueCategoryIsValueSimValueOrExpr> ValueCategoryCommon<(T,)> for ValueCategoryValueless {
|
|
||||||
type Common = ValueCategoryValueless;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ValueCategoryDispatch: Sized {
|
|
||||||
type Type: Type;
|
|
||||||
type InputValueCategory: ValueCategory;
|
|
||||||
type Value: ValueType<ValueCategory: ValueCategoryIsValueSimValueExprOrValueless, Type = Self::Type>;
|
|
||||||
type SimValue: ValueType<ValueCategory: ValueCategoryIsSimValueExprOrValueless, Type = Self::Type>;
|
|
||||||
type Expr: ValueType<ValueCategory: ValueCategoryIsExprOrValueless, Type = Self::Type>;
|
|
||||||
type Valueless: ValueType<ValueCategory: ValueCategoryIsValueless, Type = Self::Type>;
|
|
||||||
fn dispatch_value(self) -> Self::Value
|
|
||||||
where
|
|
||||||
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryValue>;
|
|
||||||
fn dispatch_sim_value(self) -> Self::SimValue
|
|
||||||
where
|
|
||||||
Self: ValueCategoryDispatch<InputValueCategory = ValueCategorySimValue>;
|
|
||||||
fn dispatch_expr(self) -> Self::Expr
|
|
||||||
where
|
|
||||||
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryExpr>;
|
|
||||||
fn dispatch_valueless(self) -> Self::Valueless
|
|
||||||
where
|
|
||||||
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryValueless>;
|
|
||||||
fn dispatch(self) -> <Self::InputValueCategory as ValueCategory>::DispatchOutput<Self> {
|
|
||||||
Self::InputValueCategory::dispatch(self)
|
|
||||||
}
|
|
||||||
fn dispatch_enum(self) -> ValueCategoryDispatchOutputEnum<Self> {
|
|
||||||
Self::InputValueCategory::dispatch_enum(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ValueCategoryDispatchOutputEnum<T: ValueCategoryDispatch> {
|
|
||||||
Value(T::Value),
|
|
||||||
SimValue(T::SimValue),
|
|
||||||
Expr(T::Expr),
|
|
||||||
Valueless(T::Valueless),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ValueCategoryDispatch> ValueCategoryDispatchOutputEnum<T> {
|
|
||||||
pub fn try_into_value(self) -> Result<T::Value, Self> {
|
|
||||||
if let Self::Value(v) = self {
|
|
||||||
Ok(v)
|
|
||||||
} else {
|
|
||||||
Err(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn try_into_sim_value(self) -> Result<T::SimValue, Self> {
|
|
||||||
if let Self::SimValue(v) = self {
|
|
||||||
Ok(v)
|
|
||||||
} else {
|
|
||||||
Err(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn try_into_expr(self) -> Result<T::Expr, Self> {
|
|
||||||
if let Self::Expr(v) = self {
|
|
||||||
Ok(v)
|
|
||||||
} else {
|
|
||||||
Err(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn try_into_valueless(self) -> Result<T::Valueless, Self> {
|
|
||||||
if let Self::Valueless(v) = self {
|
|
||||||
Ok(v)
|
|
||||||
} else {
|
|
||||||
Err(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
#[cold]
|
|
||||||
fn unwrap_invalid(self, expected_kind: &'static str) -> ! {
|
|
||||||
match self {
|
|
||||||
Self::Value(_) => panic!("expected {expected_kind}, got Value"),
|
|
||||||
Self::SimValue(_) => panic!("expected {expected_kind}, got SimValue"),
|
|
||||||
Self::Expr(_) => panic!("expected {expected_kind}, got Expr"),
|
|
||||||
Self::Valueless(_) => panic!("expected {expected_kind}, got Valueless"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn into_value_unwrap(self) -> T::Value {
|
|
||||||
let Self::Value(v) = self else {
|
|
||||||
self.unwrap_invalid("Value");
|
|
||||||
};
|
|
||||||
v
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn into_sim_value_unwrap(self) -> T::SimValue {
|
|
||||||
let Self::SimValue(v) = self else {
|
|
||||||
self.unwrap_invalid("SimValue");
|
|
||||||
};
|
|
||||||
v
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn into_expr_unwrap(self) -> T::Expr {
|
|
||||||
let Self::Expr(v) = self else {
|
|
||||||
self.unwrap_invalid("Expr");
|
|
||||||
};
|
|
||||||
v
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn into_valueless_unwrap(self) -> T::Valueless {
|
|
||||||
let Self::Valueless(v) = self else {
|
|
||||||
self.unwrap_invalid("Valueless");
|
|
||||||
};
|
|
||||||
v
|
|
||||||
}
|
|
||||||
pub fn ty(&self) -> T::Type {
|
|
||||||
match self {
|
|
||||||
Self::Value(v) => v.ty(),
|
|
||||||
Self::SimValue(v) => v.ty(),
|
|
||||||
Self::Expr(v) => v.ty(),
|
|
||||||
Self::Valueless(v) => v.ty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
type Identity<T> = T;
|
|
||||||
|
|
||||||
macro_rules! check_categories {
|
|
||||||
($($Categories:ident,)+) => {
|
|
||||||
check_categories!([] [$($Categories,)+]);
|
|
||||||
};
|
|
||||||
([$($Categories:ident,)*] []) => {};
|
|
||||||
([$($WeakerCategories:ident,)*] [$Category:ident, $($StrongerCategories:ident,)*]) => {
|
|
||||||
$(const _: $Category = Identity::<<$WeakerCategories as ValueCategoryCommon<($Category,)>>::Common> {};
|
|
||||||
const _: $Category = Identity::<<$Category as ValueCategoryCommon<($WeakerCategories,)>>::Common> {};)*
|
|
||||||
const _: $Category = Identity::<<$Category as ValueCategoryCommon<($Category,)>>::Common> {};
|
|
||||||
check_categories!([$($WeakerCategories,)* $Category,] [$($StrongerCategories,)*]);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
check_categories!(
|
|
||||||
ValueCategoryValue,
|
|
||||||
ValueCategorySimValue,
|
|
||||||
ValueCategoryExpr,
|
|
||||||
ValueCategoryValueless,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,189 +1,11 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::target::{GetTarget, Target},
|
|
||||||
int::BoolOrIntType,
|
int::BoolOrIntType,
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned, Memoize},
|
||||||
module::{NameId, NameIdOrGlobal, ScopedNameId},
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use std::{fmt, sync::OnceLock};
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub enum FormalInputKind {
|
|
||||||
FormalGlobalClock,
|
|
||||||
FormalReset,
|
|
||||||
AnyConst,
|
|
||||||
AnySeq,
|
|
||||||
AllConst,
|
|
||||||
AllSeq,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormalInputKind {
|
|
||||||
pub fn fixed_ty(self) -> Option<CanonicalType> {
|
|
||||||
match self {
|
|
||||||
Self::FormalGlobalClock => Some(Clock.into()),
|
|
||||||
Self::FormalReset => Some(SyncReset.into()),
|
|
||||||
Self::AnyConst => None,
|
|
||||||
Self::AnySeq => None,
|
|
||||||
Self::AllConst => None,
|
|
||||||
Self::AllSeq => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn fixed_id(self) -> Option<crate::module::Id> {
|
|
||||||
struct Cache {
|
|
||||||
formal_global_clock: crate::module::Id,
|
|
||||||
formal_reset: crate::module::Id,
|
|
||||||
}
|
|
||||||
static CACHE: OnceLock<Cache> = OnceLock::new();
|
|
||||||
let cache = || {
|
|
||||||
CACHE.get_or_init(
|
|
||||||
#[cold]
|
|
||||||
|| Cache {
|
|
||||||
formal_global_clock: crate::module::Id::new(),
|
|
||||||
formal_reset: crate::module::Id::new(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
match self {
|
|
||||||
Self::FormalGlobalClock => Some(cache().formal_global_clock),
|
|
||||||
Self::FormalReset => Some(cache().formal_reset),
|
|
||||||
Self::AnyConst => None,
|
|
||||||
Self::AnySeq => None,
|
|
||||||
Self::AllConst => None,
|
|
||||||
Self::AllSeq => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn fixed_source_location(self) -> Option<SourceLocation> {
|
|
||||||
match self {
|
|
||||||
Self::FormalGlobalClock | Self::FormalReset => Some(SourceLocation::builtin()),
|
|
||||||
Self::AnyConst | Self::AnySeq | Self::AllConst | Self::AllSeq => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn name(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Self::FormalGlobalClock => "formal_global_clock",
|
|
||||||
Self::FormalReset => "formal_reset",
|
|
||||||
Self::AnyConst => "any_const",
|
|
||||||
Self::AnySeq => "any_seq",
|
|
||||||
Self::AllConst => "all_const",
|
|
||||||
Self::AllSeq => "all_seq",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn interned_name(self) -> Interned<str> {
|
|
||||||
macro_rules! impl_interned_name {
|
|
||||||
($($variant:ident,)*) => {
|
|
||||||
match self {
|
|
||||||
$(Self::$variant => {
|
|
||||||
static CACHE: OnceLock<Interned<str>> = OnceLock::new();
|
|
||||||
*CACHE.get_or_init(|| Self::$variant.name().intern())
|
|
||||||
})*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
impl_interned_name! {
|
|
||||||
FormalGlobalClock,
|
|
||||||
FormalReset,
|
|
||||||
AnyConst,
|
|
||||||
AnySeq,
|
|
||||||
AllConst,
|
|
||||||
AllSeq,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
||||||
struct FormalInputData {
|
|
||||||
kind: FormalInputKind,
|
|
||||||
name_id: NameId,
|
|
||||||
ty: CanonicalType,
|
|
||||||
source_location: SourceLocation,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct FormalInput(Interned<FormalInputData>);
|
|
||||||
|
|
||||||
impl fmt::Debug for FormalInput {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
if self.kind().fixed_ty().is_some() {
|
|
||||||
f.write_str(&self.name())
|
|
||||||
} else {
|
|
||||||
f.debug_tuple(&self.name()).field(&self.0.ty).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormalInput {
|
|
||||||
#[track_caller]
|
|
||||||
pub fn new(
|
|
||||||
kind: FormalInputKind,
|
|
||||||
name_id: NameId,
|
|
||||||
ty: CanonicalType,
|
|
||||||
source_location: SourceLocation,
|
|
||||||
) -> Self {
|
|
||||||
let NameId(name, id) = name_id;
|
|
||||||
assert_eq!(kind.interned_name(), name);
|
|
||||||
if let Some(fixed_ty) = kind.fixed_ty() {
|
|
||||||
assert_eq!(ty, fixed_ty);
|
|
||||||
} else {
|
|
||||||
assert!(
|
|
||||||
ty.is_castable_from_bits(),
|
|
||||||
"{name} type must be castable from bits. got:\n{ty:#?}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(fixed_source_location) = kind.fixed_source_location() {
|
|
||||||
assert_eq!(source_location, fixed_source_location);
|
|
||||||
}
|
|
||||||
if let Some(fixed_id) = kind.fixed_id() {
|
|
||||||
assert_eq!(id, fixed_id);
|
|
||||||
}
|
|
||||||
Self(
|
|
||||||
FormalInputData {
|
|
||||||
kind,
|
|
||||||
name_id,
|
|
||||||
ty,
|
|
||||||
source_location,
|
|
||||||
}
|
|
||||||
.intern_sized(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn kind(self) -> FormalInputKind {
|
|
||||||
self.0.kind
|
|
||||||
}
|
|
||||||
pub fn name(self) -> Interned<str> {
|
|
||||||
self.0.name_id.0
|
|
||||||
}
|
|
||||||
pub fn name_id(self) -> NameId {
|
|
||||||
self.0.name_id
|
|
||||||
}
|
|
||||||
pub fn scoped_name(self) -> ScopedNameId {
|
|
||||||
ScopedNameId(NameIdOrGlobal::Global, self.name_id())
|
|
||||||
}
|
|
||||||
pub fn source_location(self) -> SourceLocation {
|
|
||||||
self.0.source_location
|
|
||||||
}
|
|
||||||
pub(crate) fn must_connect_to(self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
pub(crate) fn flow(self) -> crate::expr::Flow {
|
|
||||||
crate::expr::Flow::Source
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValueType for FormalInput {
|
|
||||||
type Type = CanonicalType;
|
|
||||||
type ValueCategory = crate::expr::value_category::ValueCategoryExpr;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
self.0.ty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetTarget for FormalInput {
|
|
||||||
fn target(&self) -> Option<Interned<Target>> {
|
|
||||||
Some(Target::from(*self).intern_sized())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub enum FormalKind {
|
pub enum FormalKind {
|
||||||
|
|
@ -316,76 +138,110 @@ make_formal!(
|
||||||
hdl_cover
|
hdl_cover
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pub trait MakeFormalExpr: Type {}
|
||||||
|
|
||||||
|
impl<T: Type> MakeFormalExpr for T {}
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub fn formal_global_clock() -> Expr<Clock> {
|
pub fn formal_global_clock() -> Expr<Clock> {
|
||||||
static CACHE: OnceLock<Expr<Clock>> = OnceLock::new();
|
#[hdl_module(extern)]
|
||||||
*CACHE.get_or_init(|| {
|
fn formal_global_clock() {
|
||||||
let kind = FormalInputKind::FormalGlobalClock;
|
#[hdl]
|
||||||
Expr::from_canonical(
|
let clk: Clock = m.output();
|
||||||
FormalInput::new(
|
m.annotate_module(BlackBoxInlineAnnotation {
|
||||||
kind,
|
path: "fayalite_formal_global_clock.v".intern(),
|
||||||
NameId(
|
text: r"module __fayalite_formal_global_clock(output clk);
|
||||||
kind.interned_name(),
|
(* gclk *)
|
||||||
kind.fixed_id().expect("known to have a fixed Id"),
|
reg clk;
|
||||||
),
|
endmodule
|
||||||
Clock.into(),
|
"
|
||||||
kind.fixed_source_location()
|
.intern(),
|
||||||
.expect("known to have a fixed SourceLocation"),
|
});
|
||||||
)
|
m.verilog_name("__fayalite_formal_global_clock");
|
||||||
.to_expr(),
|
}
|
||||||
)
|
#[hdl]
|
||||||
})
|
let formal_global_clock = instance(formal_global_clock());
|
||||||
|
formal_global_clock.clk
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub fn formal_reset() -> Expr<SyncReset> {
|
pub fn formal_reset() -> Expr<SyncReset> {
|
||||||
static CACHE: OnceLock<Expr<SyncReset>> = OnceLock::new();
|
#[hdl_module(extern)]
|
||||||
*CACHE.get_or_init(|| {
|
fn formal_reset() {
|
||||||
let kind = FormalInputKind::FormalReset;
|
#[hdl]
|
||||||
Expr::from_canonical(
|
let rst: SyncReset = m.output();
|
||||||
FormalInput::new(
|
m.annotate_module(BlackBoxInlineAnnotation {
|
||||||
kind,
|
path: "fayalite_formal_reset.v".intern(),
|
||||||
NameId(
|
text: r"module __fayalite_formal_reset(output rst);
|
||||||
kind.interned_name(),
|
assign rst = $initstate;
|
||||||
kind.fixed_id().expect("known to have a fixed Id"),
|
endmodule
|
||||||
),
|
"
|
||||||
SyncReset.into(),
|
.intern(),
|
||||||
kind.fixed_source_location()
|
});
|
||||||
.expect("known to have a fixed SourceLocation"),
|
m.verilog_name("__fayalite_formal_reset");
|
||||||
)
|
}
|
||||||
.to_expr(),
|
static MOD: OnceLock<Interned<Module<formal_reset>>> = OnceLock::new();
|
||||||
)
|
#[hdl]
|
||||||
})
|
let formal_reset = instance(*MOD.get_or_init(formal_reset));
|
||||||
|
formal_reset.rst
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! make_any_const_fn {
|
macro_rules! make_any_const_fn {
|
||||||
($ident:ident, $ident_with_loc:ident, $verilog_attribute:literal, $kind:ident) => {
|
($ident:ident, $verilog_attribute:literal) => {
|
||||||
#[track_caller]
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub fn $ident<T: BoolOrIntType>(ty: T) -> Expr<T> {
|
pub fn $ident<T: BoolOrIntType>(ty: T) -> Expr<T> {
|
||||||
$ident_with_loc(ty, SourceLocation::caller())
|
#[hdl_module(extern)]
|
||||||
}
|
pub(super) fn $ident<T: BoolOrIntType>(ty: T) {
|
||||||
#[track_caller]
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub fn $ident_with_loc<T: BoolOrIntType>(
|
let out: T = m.output(ty);
|
||||||
ty: T,
|
let width = ty.width();
|
||||||
source_location: SourceLocation,
|
let verilog_bitslice = if width == 1 {
|
||||||
) -> Expr<T> {
|
String::new()
|
||||||
let kind = FormalInputKind::$kind;
|
} else {
|
||||||
Expr::from_canonical(
|
format!(" [{}:0]", width - 1)
|
||||||
FormalInput::new(
|
};
|
||||||
kind,
|
m.annotate_module(BlackBoxInlineAnnotation {
|
||||||
NameId(kind.interned_name(), crate::module::Id::new()),
|
path: Intern::intern_owned(format!(
|
||||||
ty.canonical(),
|
"fayalite_{}_{width}.v",
|
||||||
source_location,
|
stringify!($ident),
|
||||||
)
|
)),
|
||||||
.to_expr(),
|
text: Intern::intern_owned(format!(
|
||||||
)
|
r"module __fayalite_{}_{width}(output{verilog_bitslice} out);
|
||||||
|
(* {} *)
|
||||||
|
reg{verilog_bitslice} out;
|
||||||
|
endmodule
|
||||||
|
",
|
||||||
|
stringify!($ident),
|
||||||
|
$verilog_attribute,
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
m.verilog_name(format!("__fayalite_{}_{width}", stringify!($ident)));
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
struct TheMemoize<T>(T);
|
||||||
|
impl<T: BoolOrIntType> Memoize for TheMemoize<T> {
|
||||||
|
type Input = ();
|
||||||
|
type InputOwned = ();
|
||||||
|
type Output = Option<Interned<Module<$ident<T>>>>;
|
||||||
|
fn inner(self, _input: &Self::Input) -> Self::Output {
|
||||||
|
if self.0.width() == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some($ident(self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let Some(module) = TheMemoize(ty).get_owned(()) else {
|
||||||
|
return 0_hdl_u0.cast_bits_to(ty);
|
||||||
|
};
|
||||||
|
#[hdl]
|
||||||
|
let $ident = instance(module);
|
||||||
|
$ident.out
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
make_any_const_fn!(any_const, any_const_with_loc, "anyconst", AnyConst);
|
make_any_const_fn!(any_const, "anyconst");
|
||||||
make_any_const_fn!(any_seq, any_seq_with_loc, "anyseq", AnySeq);
|
make_any_const_fn!(any_seq, "anyseq");
|
||||||
make_any_const_fn!(all_const, all_const_with_loc, "allconst", AllConst);
|
make_any_const_fn!(all_const, "allconst");
|
||||||
make_any_const_fn!(all_seq, all_seq_with_loc, "allseq", AllSeq);
|
make_any_const_fn!(all_seq, "allseq");
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -4,17 +4,17 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder},
|
bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder},
|
||||||
expr::{
|
expr::{
|
||||||
CastBitsTo, CastTo, CastToBits, CastToImpl, Expr, HdlPartialEq, HdlPartialEqImpl,
|
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd,
|
||||||
HdlPartialOrd, HdlPartialOrdImpl,
|
ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd},
|
||||||
},
|
},
|
||||||
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType, UIntValue},
|
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType},
|
||||||
intern::{Intern, InternSlice, Interned},
|
intern::{Intern, Interned},
|
||||||
phantom_const::PhantomConst,
|
phantom_const::PhantomConst,
|
||||||
sim::value::{SimValue, ToSimValueWithType},
|
sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{
|
ty::{
|
||||||
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
|
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
|
||||||
SimValueDebug, StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bitvec::{order::Lsb0, view::BitView};
|
use bitvec::{order::Lsb0, view::BitView};
|
||||||
|
|
@ -22,7 +22,7 @@ use serde::{
|
||||||
Deserialize, Deserializer, Serialize, Serializer,
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
de::{Error, Visitor, value::UsizeDeserializer},
|
de::{Error, Visitor, value::UsizeDeserializer},
|
||||||
};
|
};
|
||||||
use std::{borrow::Cow, fmt, marker::PhantomData, ops::Index};
|
use std::{fmt, marker::PhantomData, ops::Index};
|
||||||
|
|
||||||
const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"];
|
const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"];
|
||||||
|
|
||||||
|
|
@ -94,17 +94,9 @@ impl Type for UIntInRangeMaskType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimValueDebug for UIntInRangeMaskType {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BundleType for UIntInRangeMaskType {
|
impl BundleType for UIntInRangeMaskType {
|
||||||
type Builder = NoBuilder;
|
type Builder = NoBuilder;
|
||||||
|
type FilledBuilder = Expr<UIntInRangeMaskType>;
|
||||||
|
|
||||||
fn fields(&self) -> Interned<[BundleField]> {
|
fn fields(&self) -> Interned<[BundleField]> {
|
||||||
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
|
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
|
||||||
|
|
@ -120,8 +112,8 @@ impl BundleType for UIntInRangeMaskType {
|
||||||
flipped: false,
|
flipped: false,
|
||||||
ty: range.canonical(),
|
ty: range.canonical(),
|
||||||
},
|
},
|
||||||
]
|
][..]
|
||||||
.intern_slice()
|
.intern()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,62 +136,33 @@ impl ToSimValueWithType<UIntInRangeMaskType> for bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CastToImpl<Bool> for UIntInRangeMaskType {
|
impl ExprCastTo<Bool> for UIntInRangeMaskType {
|
||||||
type ValueOutput = bool;
|
fn cast_to(src: Expr<Self>, to_type: Bool) -> Expr<Bool> {
|
||||||
|
src.cast_to_bits().cast_to(to_type)
|
||||||
fn cast_value_to(
|
|
||||||
_this: Self,
|
|
||||||
value: Cow<'_, Self::SimValue>,
|
|
||||||
_to_type: Bool,
|
|
||||||
) -> Self::ValueOutput {
|
|
||||||
*value
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast_expr_to(value: Expr<Self>, to_type: Bool) -> Expr<Bool> {
|
|
||||||
value.cast_to_bits().cast_to(to_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CastToImpl<UIntInRangeMaskType> for Bool {
|
impl ExprCastTo<UIntInRangeMaskType> for Bool {
|
||||||
type ValueOutput = SimValue<UIntInRangeMaskType>;
|
fn cast_to(src: Expr<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> {
|
||||||
|
src.cast_to_static::<UInt<1>>().cast_bits_to(to_type)
|
||||||
fn cast_value_to(
|
|
||||||
_this: Self,
|
|
||||||
value: Cow<'_, Self::SimValue>,
|
|
||||||
to_type: UIntInRangeMaskType,
|
|
||||||
) -> Self::ValueOutput {
|
|
||||||
SimValue::from_value(to_type, *value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast_expr_to(value: Expr<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> {
|
|
||||||
value.cast_to_static::<UInt<1>>().cast_bits_to(to_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HdlPartialEqImpl<Self> for UIntInRangeMaskType {
|
impl ExprPartialEq<Self> for UIntInRangeMaskType {
|
||||||
const TRY_STRUCTURAL_EQ: bool = true;
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_eq(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: Self,
|
|
||||||
rhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
*lhs_value == *rhs_value
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
|
||||||
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
|
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
|
||||||
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
|
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SimValuePartialEq<Self> for UIntInRangeMaskType {
|
||||||
|
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
|
||||||
|
**this == **other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type PhantomConstRangeMaskType = <PhantomConst<SerdeRange<DynSize, DynSize>> as Type>::MaskType;
|
type PhantomConstRangeMaskType = <PhantomConst<SerdeRange<DynSize, DynSize>> as Type>::MaskType;
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, Debug)]
|
#[derive(Default, Copy, Clone, Debug)]
|
||||||
|
|
@ -337,25 +300,9 @@ macro_rules! define_uint_in_range_type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new(start: Start::SizeType, end: End::SizeType) -> Self {
|
pub fn new(start: Start::SizeType, end: End::SizeType) -> Self {
|
||||||
Self::from_phantom_const_range(PhantomConst::new_sized($SerdeRange { start, end }))
|
Self::from_phantom_const_range(PhantomConst::new(
|
||||||
}
|
$SerdeRange { start, end }.intern_sized(),
|
||||||
pub fn bit_width(self) -> usize {
|
))
|
||||||
self.value.width()
|
|
||||||
}
|
|
||||||
pub fn start(self) -> Start::SizeType {
|
|
||||||
self.range.get().start
|
|
||||||
}
|
|
||||||
pub fn end(self) -> End::SizeType {
|
|
||||||
self.range.get().end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Start: Size, End: Size> SimValueDebug for $UIntInRangeType<Start, End> {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -446,6 +393,7 @@ macro_rules! define_uint_in_range_type {
|
||||||
|
|
||||||
impl<Start: Size, End: Size> BundleType for $UIntInRangeType<Start, End> {
|
impl<Start: Size, End: Size> BundleType for $UIntInRangeType<Start, End> {
|
||||||
type Builder = NoBuilder;
|
type Builder = NoBuilder;
|
||||||
|
type FilledBuilder = Expr<Self>;
|
||||||
|
|
||||||
fn fields(&self) -> Interned<[BundleField]> {
|
fn fields(&self) -> Interned<[BundleField]> {
|
||||||
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
|
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
|
||||||
|
|
@ -461,8 +409,8 @@ macro_rules! define_uint_in_range_type {
|
||||||
flipped: false,
|
flipped: false,
|
||||||
ty: range.canonical(),
|
ty: range.canonical(),
|
||||||
},
|
},
|
||||||
]
|
][..]
|
||||||
.intern_slice()
|
.intern()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -529,66 +477,32 @@ macro_rules! define_uint_in_range_type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Start: Size, End: Size, Width: Size> CastToImpl<UIntType<Width>>
|
impl<Start: Size, End: Size> ExprCastTo<UInt> for $UIntInRangeType<Start, End> {
|
||||||
for $UIntInRangeType<Start, End>
|
fn cast_to(src: Expr<Self>, to_type: UInt) -> Expr<UInt> {
|
||||||
{
|
src.cast_to_bits().cast_to(to_type)
|
||||||
type ValueOutput = UIntValue<Width>;
|
|
||||||
|
|
||||||
fn cast_value_to(
|
|
||||||
_this: Self,
|
|
||||||
value: Cow<'_, Self::SimValue>,
|
|
||||||
to_type: UIntType<Width>,
|
|
||||||
) -> Self::ValueOutput {
|
|
||||||
value.cast_to(to_type)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast_expr_to(value: Expr<Self>, to_type: UIntType<Width>) -> Expr<UIntType<Width>> {
|
|
||||||
value.cast_to_bits().cast_to(to_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Start: Size, End: Size, Width: Size> CastToImpl<$UIntInRangeType<Start, End>>
|
impl<Start: Size, End: Size> ExprCastTo<$UIntInRangeType<Start, End>> for UInt {
|
||||||
for UIntType<Width>
|
fn cast_to(
|
||||||
{
|
src: Expr<Self>,
|
||||||
type ValueOutput = SimValue<$UIntInRangeType<Start, End>>;
|
|
||||||
|
|
||||||
fn cast_value_to(
|
|
||||||
_this: Self,
|
|
||||||
value: Cow<'_, Self::SimValue>,
|
|
||||||
to_type: $UIntInRangeType<Start, End>,
|
|
||||||
) -> Self::ValueOutput {
|
|
||||||
value.cast_to(to_type.value).cast_bits_to(to_type)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast_expr_to(
|
|
||||||
value: Expr<Self>,
|
|
||||||
to_type: $UIntInRangeType<Start, End>,
|
to_type: $UIntInRangeType<Start, End>,
|
||||||
) -> Expr<$UIntInRangeType<Start, End>> {
|
) -> Expr<$UIntInRangeType<Start, End>> {
|
||||||
value.cast_to(to_type.value).cast_bits_to(to_type)
|
src.cast_bits_to(to_type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
|
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
|
||||||
HdlPartialEqImpl<$UIntInRangeType<RhsStart, RhsEnd>>
|
ExprPartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
|
||||||
for $UIntInRangeType<LhsStart, LhsEnd>
|
for $UIntInRangeType<LhsStart, LhsEnd>
|
||||||
{
|
{
|
||||||
const TRY_STRUCTURAL_EQ: bool = true;
|
fn cmp_eq(
|
||||||
|
|
||||||
fn cmp_value_eq(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
*lhs_value == *rhs_value
|
|
||||||
}
|
|
||||||
fn cmp_expr_eq(
|
|
||||||
lhs: Expr<Self>,
|
lhs: Expr<Self>,
|
||||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||||
) -> Expr<Bool> {
|
) -> Expr<Bool> {
|
||||||
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
|
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
fn cmp_expr_ne(
|
fn cmp_ne(
|
||||||
lhs: Expr<Self>,
|
lhs: Expr<Self>,
|
||||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||||
) -> Expr<Bool> {
|
) -> Expr<Bool> {
|
||||||
|
|
@ -597,60 +511,28 @@ macro_rules! define_uint_in_range_type {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
|
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
|
||||||
HdlPartialOrdImpl<$UIntInRangeType<RhsStart, RhsEnd>>
|
ExprPartialOrd<$UIntInRangeType<RhsStart, RhsEnd>>
|
||||||
for $UIntInRangeType<LhsStart, LhsEnd>
|
for $UIntInRangeType<LhsStart, LhsEnd>
|
||||||
{
|
{
|
||||||
fn cmp_value_lt(
|
fn cmp_lt(
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
PartialOrd::lt(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_le(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
PartialOrd::le(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_gt(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
PartialOrd::gt(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_ge(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
PartialOrd::ge(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_expr_lt(
|
|
||||||
lhs: Expr<Self>,
|
lhs: Expr<Self>,
|
||||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||||
) -> Expr<Bool> {
|
) -> Expr<Bool> {
|
||||||
lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits())
|
lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
fn cmp_expr_le(
|
fn cmp_le(
|
||||||
lhs: Expr<Self>,
|
lhs: Expr<Self>,
|
||||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||||
) -> Expr<Bool> {
|
) -> Expr<Bool> {
|
||||||
lhs.cast_to_bits().cmp_le(rhs.cast_to_bits())
|
lhs.cast_to_bits().cmp_le(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
fn cmp_expr_gt(
|
fn cmp_gt(
|
||||||
lhs: Expr<Self>,
|
lhs: Expr<Self>,
|
||||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||||
) -> Expr<Bool> {
|
) -> Expr<Bool> {
|
||||||
lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits())
|
lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
fn cmp_expr_ge(
|
fn cmp_ge(
|
||||||
lhs: Expr<Self>,
|
lhs: Expr<Self>,
|
||||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||||
) -> Expr<Bool> {
|
) -> Expr<Bool> {
|
||||||
|
|
@ -658,142 +540,70 @@ macro_rules! define_uint_in_range_type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<UIntType<Width>>
|
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
|
||||||
|
SimValuePartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
|
||||||
|
for $UIntInRangeType<LhsStart, LhsEnd>
|
||||||
|
{
|
||||||
|
fn sim_value_eq(
|
||||||
|
this: &SimValue<Self>,
|
||||||
|
other: &SimValue<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||||
|
) -> bool {
|
||||||
|
**this == **other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Start: Size, End: Size, Width: Size> ExprPartialEq<UIntType<Width>>
|
||||||
for $UIntInRangeType<Start, End>
|
for $UIntInRangeType<Start, End>
|
||||||
{
|
{
|
||||||
const TRY_STRUCTURAL_EQ: bool = false;
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||||
|
|
||||||
fn cmp_value_eq(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: UIntType<Width>,
|
|
||||||
rhs_value: Cow<'_, UIntValue<Width>>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialEq::cmp_eq(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
|
||||||
lhs.cast_to_bits().cmp_eq(rhs)
|
lhs.cast_to_bits().cmp_eq(rhs)
|
||||||
}
|
}
|
||||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||||
lhs.cast_to_bits().cmp_ne(rhs)
|
lhs.cast_to_bits().cmp_ne(rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<$UIntInRangeType<Start, End>>
|
impl<Start: Size, End: Size, Width: Size> ExprPartialEq<$UIntInRangeType<Start, End>>
|
||||||
for UIntType<Width>
|
for UIntType<Width>
|
||||||
{
|
{
|
||||||
const TRY_STRUCTURAL_EQ: bool = false;
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||||
|
|
||||||
fn cmp_value_eq(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<Start, End>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialEq::cmp_eq(&*lhs_value, *rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
|
||||||
lhs.cmp_eq(rhs.cast_to_bits())
|
lhs.cmp_eq(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||||
lhs.cmp_ne(rhs.cast_to_bits())
|
lhs.cmp_ne(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Start: Size, End: Size, Width: Size> HdlPartialOrdImpl<UIntType<Width>>
|
impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<UIntType<Width>>
|
||||||
for $UIntInRangeType<Start, End>
|
for $UIntInRangeType<Start, End>
|
||||||
{
|
{
|
||||||
fn cmp_value_lt(
|
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: UIntType<Width>,
|
|
||||||
rhs_value: Cow<'_, UIntValue<Width>>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialOrd::cmp_lt(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_le(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: UIntType<Width>,
|
|
||||||
rhs_value: Cow<'_, UIntValue<Width>>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialOrd::cmp_le(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_gt(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: UIntType<Width>,
|
|
||||||
rhs_value: Cow<'_, UIntValue<Width>>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialOrd::cmp_gt(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_ge(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: UIntType<Width>,
|
|
||||||
rhs_value: Cow<'_, UIntValue<Width>>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialOrd::cmp_ge(&*lhs_value, &*rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
|
||||||
lhs.cast_to_bits().cmp_lt(rhs)
|
lhs.cast_to_bits().cmp_lt(rhs)
|
||||||
}
|
}
|
||||||
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
fn cmp_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||||
lhs.cast_to_bits().cmp_le(rhs)
|
lhs.cast_to_bits().cmp_le(rhs)
|
||||||
}
|
}
|
||||||
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||||
lhs.cast_to_bits().cmp_gt(rhs)
|
lhs.cast_to_bits().cmp_gt(rhs)
|
||||||
}
|
}
|
||||||
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||||
lhs.cast_to_bits().cmp_ge(rhs)
|
lhs.cast_to_bits().cmp_ge(rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Start: Size, End: Size, Width: Size> HdlPartialOrdImpl<$UIntInRangeType<Start, End>>
|
impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<$UIntInRangeType<Start, End>>
|
||||||
for UIntType<Width>
|
for UIntType<Width>
|
||||||
{
|
{
|
||||||
fn cmp_value_lt(
|
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<Start, End>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialOrd::cmp_lt(&*lhs_value, *rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_le(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<Start, End>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialOrd::cmp_le(&*lhs_value, *rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_gt(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<Start, End>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialOrd::cmp_gt(&*lhs_value, *rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_value_ge(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: $UIntInRangeType<Start, End>,
|
|
||||||
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
HdlPartialOrd::cmp_ge(&*lhs_value, *rhs_value)
|
|
||||||
}
|
|
||||||
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
|
||||||
lhs.cmp_lt(rhs.cast_to_bits())
|
lhs.cmp_lt(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
fn cmp_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||||
lhs.cmp_le(rhs.cast_to_bits())
|
lhs.cmp_le(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||||
lhs.cmp_gt(rhs.cast_to_bits())
|
lhs.cmp_gt(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||||
lhs.cmp_ge(rhs.cast_to_bits())
|
lhs.cmp_ge(rhs.cast_to_bits())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,191 +4,66 @@
|
||||||
use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher};
|
use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher};
|
||||||
use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec};
|
use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec};
|
||||||
use hashbrown::HashTable;
|
use hashbrown::HashTable;
|
||||||
use once_cell::race::OnceRef;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
borrow::{Borrow, Cow},
|
borrow::{Borrow, Cow},
|
||||||
cell::RefCell,
|
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
ffi::{OsStr, OsString},
|
|
||||||
fmt,
|
fmt,
|
||||||
hash::{BuildHasher, Hash, Hasher},
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
iter::FusedIterator,
|
iter::FusedIterator,
|
||||||
|
marker::PhantomData,
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
path::{Path, PathBuf},
|
sync::{Mutex, RwLock},
|
||||||
sync::RwLock,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mod interner;
|
|
||||||
mod type_map;
|
mod type_map;
|
||||||
|
|
||||||
/// invariant: T must be zero-sized, `type_id` is unique for every possible T value.
|
pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any {
|
||||||
struct LazyInternedLazyInner<T: ?Sized + 'static> {
|
fn get(&self) -> Interned<T>;
|
||||||
type_id: TypeId,
|
|
||||||
value: T,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + 'static> Hash for LazyInternedLazyInner<T> {
|
impl<T: ?Sized + Send + Sync + 'static, F: ?Sized + Fn() -> Interned<T> + Send + Sync + Any>
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
LazyInternedTrait<T> for F
|
||||||
let Self { type_id, value: _ } = self;
|
{
|
||||||
type_id.hash(state);
|
fn get(&self) -> Interned<T> {
|
||||||
|
self()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + 'static> PartialEq for LazyInternedLazyInner<T> {
|
#[repr(transparent)]
|
||||||
fn eq(&self, other: &Self) -> bool {
|
pub struct LazyInternedFn<T: ?Sized + Send + Sync + 'static>(pub &'static dyn LazyInternedTrait<T>);
|
||||||
let Self { type_id, value: _ } = self;
|
|
||||||
*type_id == other.type_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + 'static> Eq for LazyInternedLazyInner<T> {}
|
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedFn<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized + 'static> fmt::Debug for LazyInternedLazyInner<T> {
|
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedFn<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("LazyInternedLazyInner")
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + 'static> LazyInternedLazyInner<T> {
|
|
||||||
const fn new(value: T) -> Self
|
|
||||||
where
|
|
||||||
T: Sized,
|
|
||||||
{
|
|
||||||
const { assert!(size_of::<T>() == 0) };
|
|
||||||
Self {
|
|
||||||
type_id: TypeId::of::<T>(),
|
|
||||||
value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LazyInternedLazy<T: ?Sized + Send + Sync + 'static>(
|
|
||||||
&'static LazyInternedLazyInner<dyn Fn() -> Interned<T> + Send + Sync>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> LazyInternedLazy<T> {
|
|
||||||
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
|
|
||||||
Self(&const { LazyInternedLazyInner::new(|| V::default().into()) })
|
|
||||||
}
|
|
||||||
pub const fn new_const_default() -> Self
|
|
||||||
where
|
|
||||||
Interned<T>: Default,
|
|
||||||
{
|
|
||||||
Self::new_const::<Interned<T>>()
|
|
||||||
}
|
|
||||||
pub fn interned(self) -> Interned<T> {
|
|
||||||
struct Map(hashbrown::HashTable<(TypeId, &'static (dyn Any + Send + Sync))>);
|
|
||||||
impl Map {
|
|
||||||
const EMPTY: Self = Self(hashbrown::HashTable::new());
|
|
||||||
fn get<T: ?Sized + Send + Sync + 'static>(
|
|
||||||
&self,
|
|
||||||
lazy_interned_lazy: LazyInternedLazy<T>,
|
|
||||||
hash: u64,
|
|
||||||
) -> Option<&'static Interned<T>> {
|
|
||||||
let &(_, v) = self.0.find(hash, |v| v.0 == lazy_interned_lazy.0.type_id)?;
|
|
||||||
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
Some(retval)
|
|
||||||
}
|
|
||||||
fn get_or_insert<T: ?Sized + Send + Sync + 'static>(
|
|
||||||
&mut self,
|
|
||||||
lazy_interned_lazy: LazyInternedLazy<T>,
|
|
||||||
hash: u64,
|
|
||||||
v: &'static Interned<T>,
|
|
||||||
) -> &'static Interned<T> {
|
|
||||||
let entry = self
|
|
||||||
.0
|
|
||||||
.entry(
|
|
||||||
hash,
|
|
||||||
|&(k, _)| k == lazy_interned_lazy.0.type_id,
|
|
||||||
|&(k, _)| type_map::TypeIdBuildHasher.hash_one(k),
|
|
||||||
)
|
|
||||||
.or_insert_with(|| (lazy_interned_lazy.0.type_id, v));
|
|
||||||
let &(_, v) = entry.get();
|
|
||||||
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
retval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static GLOBAL_CACHE: RwLock<Map> = RwLock::new(Map::EMPTY);
|
|
||||||
#[cold]
|
|
||||||
fn insert_in_thread_local_cache<T: ?Sized + Send + Sync + 'static>(
|
|
||||||
cache: &RefCell<Map>,
|
|
||||||
this: LazyInternedLazy<T>,
|
|
||||||
hash: u64,
|
|
||||||
) -> Interned<T> {
|
|
||||||
let read_lock = GLOBAL_CACHE.read().unwrap();
|
|
||||||
let v = read_lock.get(this, hash);
|
|
||||||
drop(read_lock);
|
|
||||||
let v = v.unwrap_or_else(|| {
|
|
||||||
let v = Box::leak(Box::new((this.0.value)()));
|
|
||||||
GLOBAL_CACHE.write().unwrap().get_or_insert(this, hash, v)
|
|
||||||
});
|
|
||||||
*cache.borrow_mut().get_or_insert(this, hash, v)
|
|
||||||
}
|
|
||||||
thread_local! {
|
|
||||||
static THREAD_LOCAL_CACHE: RefCell<Map> = const { RefCell::new(Map::EMPTY) };
|
|
||||||
}
|
|
||||||
let hash = type_map::TypeIdBuildHasher.hash_one(self.0.type_id);
|
|
||||||
THREAD_LOCAL_CACHE.with(|cache| {
|
|
||||||
let borrow = cache.borrow();
|
|
||||||
if let Some(v) = borrow.get(self, hash) {
|
|
||||||
*v
|
|
||||||
} else {
|
|
||||||
drop(borrow);
|
|
||||||
insert_in_thread_local_cache(cache, self, hash)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedLazy<T> {}
|
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedLazy<T> {
|
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedLazy<T> {
|
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedFn<T> {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.0.hash(state);
|
self.0.get_ptr_eq_with_type_id().hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedLazy<T> {}
|
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedFn<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedLazy<T> {
|
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedFn<T> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.0 == other.0
|
self.0.get_ptr_eq_with_type_id() == other.0.get_ptr_eq_with_type_id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> {
|
pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> {
|
||||||
Interned(Interned<T>),
|
Interned(Interned<T>),
|
||||||
Lazy(LazyInternedLazy<T>),
|
Lazy(LazyInternedFn<T>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> {
|
impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> {
|
||||||
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
|
pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
|
||||||
Self::Lazy(LazyInternedLazy::new_const::<V>())
|
Self::Lazy(LazyInternedFn(v))
|
||||||
}
|
|
||||||
pub const fn new_const_default() -> Self
|
|
||||||
where
|
|
||||||
Interned<T>: Default,
|
|
||||||
{
|
|
||||||
Self::new_const::<Interned<T>>()
|
|
||||||
}
|
|
||||||
pub fn interned(self) -> Interned<T> {
|
|
||||||
match self {
|
|
||||||
Self::Interned(retval) => retval,
|
|
||||||
Self::Lazy(retval) => retval.interned(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +75,7 @@ impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> {
|
||||||
|
|
||||||
impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {}
|
impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized + Sync + Send + 'static> Deref for LazyInterned<T> {
|
impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
|
@ -208,9 +83,9 @@ impl<T: ?Sized + Sync + Send + 'static> Deref for LazyInterned<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Sync + Send + 'static> Eq for LazyInterned<T> where Interned<T>: Eq {}
|
impl<T: ?Sized + Sync + Send + 'static + Intern> Eq for LazyInterned<T> where Interned<T>: Eq {}
|
||||||
|
|
||||||
impl<T: ?Sized + Sync + Send + 'static> PartialEq for LazyInterned<T>
|
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialEq for LazyInterned<T>
|
||||||
where
|
where
|
||||||
Interned<T>: PartialEq,
|
Interned<T>: PartialEq,
|
||||||
{
|
{
|
||||||
|
|
@ -219,7 +94,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Sync + Send + 'static> Ord for LazyInterned<T>
|
impl<T: ?Sized + Sync + Send + 'static + Intern> Ord for LazyInterned<T>
|
||||||
where
|
where
|
||||||
Interned<T>: Ord,
|
Interned<T>: Ord,
|
||||||
{
|
{
|
||||||
|
|
@ -228,7 +103,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Sync + Send + 'static> PartialOrd for LazyInterned<T>
|
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialOrd for LazyInterned<T>
|
||||||
where
|
where
|
||||||
Interned<T>: PartialOrd,
|
Interned<T>: PartialOrd,
|
||||||
{
|
{
|
||||||
|
|
@ -237,7 +112,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Sync + Send + 'static> Hash for LazyInterned<T>
|
impl<T: ?Sized + Sync + Send + 'static + Intern> Hash for LazyInterned<T>
|
||||||
where
|
where
|
||||||
Interned<T>: Hash,
|
Interned<T>: Hash,
|
||||||
{
|
{
|
||||||
|
|
@ -246,6 +121,77 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Sync + Send + 'static> LazyInterned<T> {
|
||||||
|
pub fn interned(self) -> Interned<T>
|
||||||
|
where
|
||||||
|
T: Intern,
|
||||||
|
{
|
||||||
|
struct MemoizeInterned<T: ?Sized + Intern>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: ?Sized + Intern> Hash for MemoizeInterned<T> {
|
||||||
|
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Intern> PartialEq for MemoizeInterned<T> {
|
||||||
|
fn eq(&self, _other: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Intern> Eq for MemoizeInterned<T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Intern> Clone for MemoizeInterned<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Intern> Copy for MemoizeInterned<T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Intern> MemoizeGeneric for MemoizeInterned<T> {
|
||||||
|
type InputRef<'a> = LazyInternedFn<T>;
|
||||||
|
|
||||||
|
type InputOwned = LazyInternedFn<T>;
|
||||||
|
|
||||||
|
type InputCow<'a> = LazyInternedFn<T>;
|
||||||
|
|
||||||
|
type Output = Interned<T>;
|
||||||
|
|
||||||
|
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
|
||||||
|
a == b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
|
||||||
|
*input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
|
||||||
|
*input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
|
||||||
|
input.0.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Interned(retval) => retval,
|
||||||
|
Self::Lazy(retval) => MemoizeInterned(PhantomData).get(retval),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait InternedCompare {
|
pub trait InternedCompare {
|
||||||
type InternedCompareKey: Ord + Hash;
|
type InternedCompareKey: Ord + Hash;
|
||||||
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey;
|
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey;
|
||||||
|
|
@ -341,266 +287,15 @@ impl InternedCompare for BitSlice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safety: `as_bytes` and `from_bytes_unchecked` must return the same pointer as the input.
|
impl InternedCompare for str {
|
||||||
/// all values returned by `as_bytes` must be valid to pass to `from_bytes_unchecked`.
|
type InternedCompareKey = PtrEqWithMetadata<Self>;
|
||||||
/// `into_bytes` must return the exact same thing as `as_bytes`.
|
|
||||||
/// `Interned<Self>` must contain the exact same references as `Interned<[u8]>`,
|
|
||||||
/// so they can be safely interconverted without needing re-interning.
|
|
||||||
unsafe trait InternStrLike: ToOwned {
|
|
||||||
fn as_bytes(this: &Self) -> &[u8];
|
|
||||||
fn into_bytes(this: Self::Owned) -> Vec<u8>;
|
|
||||||
/// Safety: `bytes` must be a valid sequence of bytes for this type. All UTF-8 sequences are valid.
|
|
||||||
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_intern_str_like {
|
|
||||||
($ty:ty, owned = $Owned:ty) => {
|
|
||||||
impl InternedCompare for $ty {
|
|
||||||
type InternedCompareKey = PtrEqWithMetadata<[u8]>;
|
|
||||||
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
|
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
|
||||||
PtrEqWithMetadata(InternStrLike::as_bytes(this))
|
PtrEqWithMetadata(this)
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Intern for $ty {
|
|
||||||
fn intern(&self) -> Interned<Self> {
|
|
||||||
Self::intern_cow(Cow::Borrowed(self))
|
|
||||||
}
|
|
||||||
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self> {
|
|
||||||
Interned::cast_unchecked(
|
|
||||||
<[u8]>::intern_cow(match this {
|
|
||||||
Cow::Borrowed(v) => Cow::Borrowed(<Self as InternStrLike>::as_bytes(v)),
|
|
||||||
Cow::Owned(v) => {
|
|
||||||
// verify $Owned is correct
|
|
||||||
let v: $Owned = v;
|
|
||||||
Cow::Owned(<Self as InternStrLike>::into_bytes(v))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
// Safety: guaranteed safe because we got the bytes from `as_bytes`/`into_bytes`
|
|
||||||
|v| unsafe { <Self as InternStrLike>::from_bytes_unchecked(v) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for Interned<$ty> {
|
|
||||||
fn default() -> Self {
|
|
||||||
// Safety: safe because the empty sequence is valid UTF-8
|
|
||||||
unsafe { <$ty as InternStrLike>::from_bytes_unchecked(&[]) }.intern()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'de> Deserialize<'de> for Interned<$ty> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
Cow::<'de, $ty>::deserialize(deserializer).map(Intern::intern_cow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<$Owned> for Interned<$ty> {
|
|
||||||
fn from(v: $Owned) -> Self {
|
|
||||||
v.intern_deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Interned<$ty>> for $Owned {
|
|
||||||
fn from(v: Interned<$ty>) -> Self {
|
|
||||||
Interned::into_inner(v).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Interned<$ty>> for Box<$ty> {
|
|
||||||
fn from(v: Interned<$ty>) -> Self {
|
|
||||||
Interned::into_inner(v).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `str`
|
|
||||||
unsafe impl InternStrLike for str {
|
|
||||||
fn as_bytes(this: &Self) -> &[u8] {
|
|
||||||
this.as_bytes()
|
|
||||||
}
|
|
||||||
fn into_bytes(this: Self::Owned) -> Vec<u8> {
|
|
||||||
this.into_bytes()
|
|
||||||
}
|
|
||||||
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
|
|
||||||
// Safety: `bytes` is guaranteed UTF-8 by the caller
|
|
||||||
unsafe { str::from_utf8_unchecked(bytes) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_intern_str_like!(str, owned = String);
|
|
||||||
|
|
||||||
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `OsStr`
|
|
||||||
unsafe impl InternStrLike for OsStr {
|
|
||||||
fn as_bytes(this: &Self) -> &[u8] {
|
|
||||||
this.as_encoded_bytes()
|
|
||||||
}
|
|
||||||
fn into_bytes(this: Self::Owned) -> Vec<u8> {
|
|
||||||
this.into_encoded_bytes()
|
|
||||||
}
|
|
||||||
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
|
|
||||||
// Safety: `bytes` is guaranteed valid for `OsStr` by the caller
|
|
||||||
unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_intern_str_like!(OsStr, owned = OsString);
|
|
||||||
|
|
||||||
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `OsStr`
|
|
||||||
unsafe impl InternStrLike for Path {
|
|
||||||
fn as_bytes(this: &Self) -> &[u8] {
|
|
||||||
this.as_os_str().as_encoded_bytes()
|
|
||||||
}
|
|
||||||
fn into_bytes(this: Self::Owned) -> Vec<u8> {
|
|
||||||
this.into_os_string().into_encoded_bytes()
|
|
||||||
}
|
|
||||||
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
|
|
||||||
// Safety: `bytes` is guaranteed valid for `OsStr` by the caller
|
|
||||||
unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(bytes)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_intern_str_like!(Path, owned = PathBuf);
|
|
||||||
|
|
||||||
impl Interned<str> {
|
|
||||||
pub fn from_utf8(v: Interned<[u8]>) -> Result<Self, std::str::Utf8Error> {
|
|
||||||
Interned::try_cast_unchecked(v, str::from_utf8)
|
|
||||||
}
|
|
||||||
pub fn as_interned_bytes(self) -> Interned<[u8]> {
|
|
||||||
Interned::cast_unchecked(self, str::as_bytes)
|
|
||||||
}
|
|
||||||
pub fn as_interned_os_str(self) -> Interned<OsStr> {
|
|
||||||
Interned::cast_unchecked(self, AsRef::as_ref)
|
|
||||||
}
|
|
||||||
pub fn as_interned_path(self) -> Interned<Path> {
|
|
||||||
Interned::cast_unchecked(self, AsRef::as_ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Interned<str>> for Interned<OsStr> {
|
|
||||||
fn from(value: Interned<str>) -> Self {
|
|
||||||
value.as_interned_os_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Interned<str>> for Interned<Path> {
|
|
||||||
fn from(value: Interned<str>) -> Self {
|
|
||||||
value.as_interned_path()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interned<OsStr> {
|
|
||||||
pub fn as_interned_encoded_bytes(self) -> Interned<[u8]> {
|
|
||||||
Interned::cast_unchecked(self, OsStr::as_encoded_bytes)
|
|
||||||
}
|
|
||||||
pub fn to_interned_str(self) -> Option<Interned<str>> {
|
|
||||||
Interned::try_cast_unchecked(self, |v| v.to_str().ok_or(())).ok()
|
|
||||||
}
|
|
||||||
pub fn display(self) -> std::ffi::os_str::Display<'static> {
|
|
||||||
Self::into_inner(self).display()
|
|
||||||
}
|
|
||||||
pub fn as_interned_path(self) -> Interned<Path> {
|
|
||||||
Interned::cast_unchecked(self, AsRef::as_ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Interned<OsStr>> for Interned<Path> {
|
|
||||||
fn from(value: Interned<OsStr>) -> Self {
|
|
||||||
value.as_interned_path()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interned<Path> {
|
|
||||||
pub fn as_interned_os_str(self) -> Interned<OsStr> {
|
|
||||||
Interned::cast_unchecked(self, AsRef::as_ref)
|
|
||||||
}
|
|
||||||
pub fn to_interned_str(self) -> Option<Interned<str>> {
|
|
||||||
Interned::try_cast_unchecked(self, |v| v.to_str().ok_or(())).ok()
|
|
||||||
}
|
|
||||||
pub fn display(self) -> std::path::Display<'static> {
|
|
||||||
Self::into_inner(self).display()
|
|
||||||
}
|
|
||||||
pub fn interned_file_name(self) -> Option<Interned<OsStr>> {
|
|
||||||
Some(self.file_name()?.intern())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Interned<Path>> for Interned<OsStr> {
|
|
||||||
fn from(value: Interned<Path>) -> Self {
|
|
||||||
value.as_interned_os_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait InternSlice: Sized {
|
|
||||||
type Element: 'static + Send + Sync + Clone + Hash + Eq;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for Box<[T]> {
|
|
||||||
type Element = T;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]> {
|
|
||||||
self.into_vec().intern_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for Vec<T> {
|
|
||||||
type Element = T;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]> {
|
|
||||||
self.intern_deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for &'_ [T] {
|
|
||||||
type Element = T;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]> {
|
|
||||||
self.intern()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for &'_ mut [T] {
|
|
||||||
type Element = T;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]> {
|
|
||||||
self.intern()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for [T; N] {
|
|
||||||
type Element = T;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]> {
|
|
||||||
(&self).intern_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for Box<[T; N]> {
|
|
||||||
type Element = T;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]> {
|
|
||||||
let this: Box<[T]> = self;
|
|
||||||
this.intern_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for &'_ [T; N] {
|
|
||||||
type Element = T;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]> {
|
|
||||||
let this: &[T] = self;
|
|
||||||
this.intern()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for &'_ mut [T; N] {
|
|
||||||
type Element = T;
|
|
||||||
fn intern_slice(self) -> Interned<[Self::Element]> {
|
|
||||||
let this: &[T] = self;
|
|
||||||
this.intern()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Intern: Any + Send + Sync {
|
pub trait Intern: Any + Send + Sync {
|
||||||
fn intern(&self) -> Interned<Self>;
|
fn intern(&self) -> Interned<Self>;
|
||||||
fn intern_deref(self) -> Interned<Self::Target>
|
|
||||||
where
|
|
||||||
Self: Sized + Deref<Target: Intern + ToOwned<Owned = Self>>,
|
|
||||||
{
|
|
||||||
Self::Target::intern_owned(self)
|
|
||||||
}
|
|
||||||
fn intern_sized(self) -> Interned<Self>
|
fn intern_sized(self) -> Interned<Self>
|
||||||
where
|
where
|
||||||
Self: Clone,
|
Self: Clone,
|
||||||
|
|
@ -621,27 +316,74 @@ pub trait Intern: Any + Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Intern + ToOwned> From<Cow<'_, T>> for Interned<T> {
|
struct InternerState<T: ?Sized + 'static + Send + Sync> {
|
||||||
fn from(value: Cow<'_, T>) -> Self {
|
table: HashTable<&'static T>,
|
||||||
Intern::intern_cow(value)
|
hasher: DefaultBuildHasher,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Interner<T: ?Sized + 'static + Send + Sync> {
|
||||||
|
state: Mutex<InternerState<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + 'static + Send + Sync> Interner<T> {
|
||||||
|
fn get() -> &'static Interner<T> {
|
||||||
|
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
|
||||||
|
TYPE_ID_MAP.get_or_insert_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Intern> From<&'_ T> for Interned<T> {
|
impl<T: ?Sized + 'static + Send + Sync> Default for Interner<T> {
|
||||||
fn from(value: &'_ T) -> Self {
|
fn default() -> Self {
|
||||||
Intern::intern(value)
|
Self {
|
||||||
|
state: Mutex::new(InternerState {
|
||||||
|
table: HashTable::new(),
|
||||||
|
hasher: Default::default(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Intern + Clone> From<T> for Interned<T> {
|
impl<T: ?Sized + 'static + Send + Sync + Hash + Eq + ToOwned> Interner<T> {
|
||||||
fn from(value: T) -> Self {
|
fn intern<F: FnOnce(Cow<'_, T>) -> &'static T>(
|
||||||
Intern::intern_sized(value)
|
&self,
|
||||||
|
alloc: F,
|
||||||
|
value: Cow<'_, T>,
|
||||||
|
) -> Interned<T> {
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
let InternerState { table, hasher } = &mut *state;
|
||||||
|
let inner = *table
|
||||||
|
.entry(
|
||||||
|
hasher.hash_one(&*value),
|
||||||
|
|k| **k == *value,
|
||||||
|
|k| hasher.hash_one(&**k),
|
||||||
|
)
|
||||||
|
.or_insert_with(|| alloc(value))
|
||||||
|
.get();
|
||||||
|
Interned { inner }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + 'static + Send + Sync + ToOwned> From<Interned<T>> for Cow<'_, T> {
|
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<T> {
|
||||||
fn from(value: Interned<T>) -> Self {
|
fn intern_sized(&self, value: Cow<'_, T>) -> Interned<T> {
|
||||||
Cow::Borrowed(Interned::into_inner(value))
|
self.intern(|value| Box::leak(Box::new(value.into_owned())), value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<[T]> {
|
||||||
|
fn intern_slice(&self, value: Cow<'_, [T]>) -> Interned<[T]> {
|
||||||
|
self.intern(|value| value.into_owned().leak(), value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interner<BitSlice> {
|
||||||
|
fn intern_bit_slice(&self, value: Cow<'_, BitSlice>) -> Interned<BitSlice> {
|
||||||
|
self.intern(|value| value.into_owned().leak(), value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interner<str> {
|
||||||
|
fn intern_str(&self, value: Cow<'_, str>) -> Interned<str> {
|
||||||
|
self.intern(|value| value.into_owned().leak(), value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -674,70 +416,29 @@ forward_fmt_trait!(Pointer);
|
||||||
forward_fmt_trait!(UpperExp);
|
forward_fmt_trait!(UpperExp);
|
||||||
forward_fmt_trait!(UpperHex);
|
forward_fmt_trait!(UpperHex);
|
||||||
|
|
||||||
impl<T: ?Sized + 'static + Send + Sync + AsRef<U>, U: ?Sized> AsRef<U> for Interned<T> {
|
|
||||||
fn as_ref(&self) -> &U {
|
|
||||||
T::as_ref(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct InternedSliceIter<T: Clone + 'static + Send + Sync> {
|
pub struct InternedSliceIter<T: Clone + 'static + Send + Sync> {
|
||||||
iter: std::iter::Cloned<std::slice::Iter<'static, T>>,
|
slice: Interned<[T]>,
|
||||||
}
|
index: std::ops::Range<usize>,
|
||||||
|
|
||||||
impl<T: Clone + 'static + Send + Sync> Default for InternedSliceIter<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
iter: [].iter().cloned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + 'static + Send + Sync> Iterator for InternedSliceIter<T> {
|
impl<T: Clone + 'static + Send + Sync> Iterator for InternedSliceIter<T> {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.iter.next()
|
self.index.next().map(|index| self.slice[index].clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
self.iter.size_hint()
|
self.index.size_hint()
|
||||||
}
|
|
||||||
|
|
||||||
fn count(self) -> usize {
|
|
||||||
self.iter.count()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last(mut self) -> Option<Self::Item> {
|
|
||||||
self.next_back()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
|
||||||
self.iter.nth(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold<B, F>(self, init: B, f: F) -> B
|
|
||||||
where
|
|
||||||
F: FnMut(B, Self::Item) -> B,
|
|
||||||
{
|
|
||||||
self.iter.fold(init, f)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + 'static + Send + Sync> DoubleEndedIterator for InternedSliceIter<T> {
|
impl<T: Clone + 'static + Send + Sync> DoubleEndedIterator for InternedSliceIter<T> {
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
self.iter.next_back()
|
self.index
|
||||||
}
|
.next_back()
|
||||||
|
.map(|index| self.slice[index].clone())
|
||||||
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
|
||||||
self.iter.nth_back(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rfold<B, F>(self, init: B, f: F) -> B
|
|
||||||
where
|
|
||||||
F: FnMut(B, Self::Item) -> B,
|
|
||||||
{
|
|
||||||
self.iter.rfold(init, f)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -751,7 +452,8 @@ impl<T: Clone + 'static + Send + Sync> IntoIterator for Interned<[T]> {
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
InternedSliceIter {
|
InternedSliceIter {
|
||||||
iter: Interned::into_inner(self).iter().cloned(),
|
index: 0..self.len(),
|
||||||
|
slice: self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -783,57 +485,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> FromIterator<I> for Interned<str>
|
|
||||||
where
|
|
||||||
String: FromIterator<I>,
|
|
||||||
{
|
|
||||||
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
|
|
||||||
String::from_iter(iter).intern_deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> FromIterator<I> for Interned<Path>
|
|
||||||
where
|
|
||||||
PathBuf: FromIterator<I>,
|
|
||||||
{
|
|
||||||
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
|
|
||||||
PathBuf::from_iter(iter).intern_deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> FromIterator<I> for Interned<OsStr>
|
|
||||||
where
|
|
||||||
OsString: FromIterator<I>,
|
|
||||||
{
|
|
||||||
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
|
|
||||||
OsString::from_iter(iter).intern_deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Interned<str>> for clap::builder::Str {
|
|
||||||
fn from(value: Interned<str>) -> Self {
|
|
||||||
Interned::into_inner(value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Interned<str>> for clap::builder::OsStr {
|
|
||||||
fn from(value: Interned<str>) -> Self {
|
|
||||||
Interned::into_inner(value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Interned<str>> for clap::builder::StyledStr {
|
|
||||||
fn from(value: Interned<str>) -> Self {
|
|
||||||
Interned::into_inner(value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Interned<str>> for clap::Id {
|
|
||||||
fn from(value: Interned<str>) -> Self {
|
|
||||||
Interned::into_inner(value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Vec<T> {
|
impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Vec<T> {
|
||||||
fn from(value: Interned<[T]>) -> Self {
|
fn from(value: Interned<[T]>) -> Self {
|
||||||
Vec::from(&*value)
|
Vec::from(&*value)
|
||||||
|
|
@ -846,12 +497,24 @@ impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Box<[T]> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Interned<str>> for String {
|
||||||
|
fn from(value: Interned<str>) -> Self {
|
||||||
|
String::from(&*value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<I> Default for Interned<[I]>
|
impl<I> Default for Interned<[I]>
|
||||||
where
|
where
|
||||||
[I]: Intern,
|
[I]: Intern,
|
||||||
{
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Intern::intern(&[])
|
[][..].intern()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Interned<str> {
|
||||||
|
fn default() -> Self {
|
||||||
|
"".intern()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -982,6 +645,15 @@ impl<'de> Deserialize<'de> for Interned<BitSlice> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Interned<str> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
String::deserialize(deserializer).map(Intern::intern_owned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T {
|
impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T {
|
||||||
fn intern(&self) -> Interned<Self> {
|
fn intern(&self) -> Interned<Self> {
|
||||||
Self::intern_cow(Cow::Borrowed(self))
|
Self::intern_cow(Cow::Borrowed(self))
|
||||||
|
|
@ -998,7 +670,7 @@ impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T {
|
||||||
where
|
where
|
||||||
Self: ToOwned,
|
Self: ToOwned,
|
||||||
{
|
{
|
||||||
interner::Interner::get().intern_sized(this)
|
Interner::get().intern_sized(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1018,7 +690,7 @@ impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for [T] {
|
||||||
where
|
where
|
||||||
Self: ToOwned,
|
Self: ToOwned,
|
||||||
{
|
{
|
||||||
interner::Interner::get().intern_slice(this)
|
Interner::get().intern_slice(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1038,7 +710,27 @@ impl Intern for BitSlice {
|
||||||
where
|
where
|
||||||
Self: ToOwned,
|
Self: ToOwned,
|
||||||
{
|
{
|
||||||
interner::Interner::get().intern_bit_slice(this)
|
Interner::get().intern_bit_slice(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Intern for str {
|
||||||
|
fn intern(&self) -> Interned<Self> {
|
||||||
|
Self::intern_cow(Cow::Borrowed(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intern_owned(this: <Self as ToOwned>::Owned) -> Interned<Self>
|
||||||
|
where
|
||||||
|
Self: ToOwned,
|
||||||
|
{
|
||||||
|
Self::intern_cow(Cow::Owned(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self>
|
||||||
|
where
|
||||||
|
Self: ToOwned,
|
||||||
|
{
|
||||||
|
Interner::get().intern_str(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1056,17 +748,10 @@ pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy {
|
||||||
fn inner(self, input: Self::InputRef<'_>) -> Self::Output;
|
fn inner(self, input: Self::InputRef<'_>) -> Self::Output;
|
||||||
fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output {
|
fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output {
|
||||||
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
|
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
|
||||||
thread_local! {
|
|
||||||
static TYPE_ID_MAP_CACHE: TypeIdMap = const { TypeIdMap::new() };
|
|
||||||
}
|
|
||||||
let map: &RwLock<(
|
let map: &RwLock<(
|
||||||
DefaultBuildHasher,
|
DefaultBuildHasher,
|
||||||
HashTable<(Self, Self::InputOwned, Self::Output)>,
|
HashTable<(Self, Self::InputOwned, Self::Output)>,
|
||||||
)> = TYPE_ID_MAP_CACHE.with(|cache| {
|
)> = TYPE_ID_MAP.get_or_insert_default();
|
||||||
cache.get_or_insert_with(|| {
|
|
||||||
TYPE_ID_MAP.get_or_insert_with(|| Box::leak(Default::default()))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
fn hash_eq_key<'a, 'b, T: MemoizeGeneric>(
|
fn hash_eq_key<'a, 'b, T: MemoizeGeneric>(
|
||||||
this: &'a T,
|
this: &'a T,
|
||||||
input: T::InputRef<'b>,
|
input: T::InputRef<'b>,
|
||||||
|
|
@ -1168,35 +853,3 @@ pub trait Memoize: 'static + Send + Sync + Hash + Eq + Copy {
|
||||||
self.get_cow(Cow::Borrowed(input))
|
self.get_cow(Cow::Borrowed(input))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// like `once_cell::race::OnceBox` but for `Interned<T>` instead of `Box<T>`
|
|
||||||
pub struct OnceInterned<T: 'static + Send + Sync>(OnceRef<'static, T>);
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync + fmt::Debug> fmt::Debug for OnceInterned<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_tuple("OnceInterned").field(&self.get()).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync> Default for OnceInterned<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Send + Sync> OnceInterned<T> {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self(OnceRef::new())
|
|
||||||
}
|
|
||||||
pub fn set(&self, v: Interned<T>) -> Result<(), ()> {
|
|
||||||
self.0.set(v.inner)
|
|
||||||
}
|
|
||||||
pub fn get(&self) -> Option<Interned<T>> {
|
|
||||||
self.0.get().map(|inner| Interned { inner })
|
|
||||||
}
|
|
||||||
pub fn get_or_init<F: FnOnce() -> Interned<T>>(&self, f: F) -> Interned<T> {
|
|
||||||
Interned {
|
|
||||||
inner: self.0.get_or_init(|| f().inner),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
intern::{Interned, type_map::TypeIdMap},
|
|
||||||
util::DefaultBuildHasher,
|
|
||||||
};
|
|
||||||
use bitvec::slice::BitSlice;
|
|
||||||
use hashbrown::HashTable;
|
|
||||||
use std::{
|
|
||||||
borrow::Cow,
|
|
||||||
hash::{BuildHasher, Hash},
|
|
||||||
sync::RwLock,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InternerShard<T: ?Sized + 'static + Send + Sync> {
|
|
||||||
table: HashTable<&'static T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const LOG2_SHARD_COUNT: u32 = 6;
|
|
||||||
|
|
||||||
fn shard_index_from_hash(hash: u64) -> usize {
|
|
||||||
// number of bits used for hashbrown's Tag
|
|
||||||
const HASH_BROWN_TAG_BITS: u32 = 7;
|
|
||||||
// try to extract bits of the hash that hashbrown isn't using,
|
|
||||||
// while accounting for some hash functions only returning `usize` bits.
|
|
||||||
const SHARD_INDEX_START: u32 = usize::BITS
|
|
||||||
.saturating_sub(HASH_BROWN_TAG_BITS)
|
|
||||||
.saturating_sub(LOG2_SHARD_COUNT);
|
|
||||||
let mut shard_index = hash >> SHARD_INDEX_START;
|
|
||||||
shard_index %= 1 << LOG2_SHARD_COUNT;
|
|
||||||
shard_index as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct Interner<T: ?Sized + 'static + Send + Sync> {
|
|
||||||
shards: [RwLock<InternerShard<T>>; 1 << LOG2_SHARD_COUNT],
|
|
||||||
hasher: DefaultBuildHasher,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + 'static + Send + Sync> Interner<T> {
|
|
||||||
pub(crate) fn get() -> &'static Interner<T> {
|
|
||||||
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
|
|
||||||
thread_local! {
|
|
||||||
static TYPE_ID_MAP_CACHE: TypeIdMap = const { TypeIdMap::new() };
|
|
||||||
}
|
|
||||||
TYPE_ID_MAP_CACHE.with(|cache| {
|
|
||||||
cache.get_or_insert_with(|| {
|
|
||||||
TYPE_ID_MAP.get_or_insert_with(|| Box::leak(Default::default()))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + 'static + Send + Sync> Default for Interner<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
shards: [const {
|
|
||||||
RwLock::new(InternerShard {
|
|
||||||
table: HashTable::new(),
|
|
||||||
})
|
|
||||||
}; _],
|
|
||||||
hasher: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + 'static + Send + Sync + Hash + Eq + ToOwned> Interner<T> {
|
|
||||||
fn intern<F: FnOnce(Cow<'_, T>) -> &'static T>(
|
|
||||||
&self,
|
|
||||||
alloc: F,
|
|
||||||
value: Cow<'_, T>,
|
|
||||||
) -> Interned<T> {
|
|
||||||
let hash = self.hasher.hash_one(&*value);
|
|
||||||
let shard_index = shard_index_from_hash(hash);
|
|
||||||
let shard = &self.shards[shard_index];
|
|
||||||
let shard_read = shard.read().unwrap();
|
|
||||||
let Some(&inner) = shard_read.table.find(hash, |k| **k == *value) else {
|
|
||||||
drop(shard_read);
|
|
||||||
return self.intern_cold(alloc, value, hash, shard);
|
|
||||||
};
|
|
||||||
Interned { inner }
|
|
||||||
}
|
|
||||||
#[cold]
|
|
||||||
fn intern_cold<F: FnOnce(Cow<'_, T>) -> &'static T>(
|
|
||||||
&self,
|
|
||||||
alloc: F,
|
|
||||||
value: Cow<'_, T>,
|
|
||||||
hash: u64,
|
|
||||||
shard: &RwLock<InternerShard<T>>,
|
|
||||||
) -> Interned<T> {
|
|
||||||
let mut shard = shard.write().unwrap();
|
|
||||||
let inner = *shard
|
|
||||||
.table
|
|
||||||
.entry(hash, |k| **k == *value, |k| self.hasher.hash_one(&**k))
|
|
||||||
.or_insert_with(|| alloc(value))
|
|
||||||
.get();
|
|
||||||
Interned { inner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<T> {
|
|
||||||
pub(crate) fn intern_sized(&self, value: Cow<'_, T>) -> Interned<T> {
|
|
||||||
self.intern(|value| Box::leak(Box::new(value.into_owned())), value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<[T]> {
|
|
||||||
pub(crate) fn intern_slice(&self, value: Cow<'_, [T]>) -> Interned<[T]> {
|
|
||||||
self.intern(|value| value.into_owned().leak(), value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interner<BitSlice> {
|
|
||||||
pub(crate) fn intern_bit_slice(&self, value: Cow<'_, BitSlice>) -> Interned<BitSlice> {
|
|
||||||
self.intern(|value| value.into_owned().leak(), value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
sync::RwLock,
|
sync::RwLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) struct TypeIdHasher(u64);
|
struct TypeIdHasher(u64);
|
||||||
|
|
||||||
// assumes TypeId has at least 64 bits that is a good hash
|
// assumes TypeId has at least 64 bits that is a good hash
|
||||||
impl Hasher for TypeIdHasher {
|
impl Hasher for TypeIdHasher {
|
||||||
|
|
@ -63,7 +63,7 @@ impl Hasher for TypeIdHasher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct TypeIdBuildHasher;
|
struct TypeIdBuildHasher;
|
||||||
|
|
||||||
impl BuildHasher for TypeIdBuildHasher {
|
impl BuildHasher for TypeIdBuildHasher {
|
||||||
type Hasher = TypeIdHasher;
|
type Hasher = TypeIdHasher;
|
||||||
|
|
@ -87,23 +87,20 @@ impl TypeIdMap {
|
||||||
fn insert_slow(
|
fn insert_slow(
|
||||||
&self,
|
&self,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
make: impl FnOnce() -> &'static (dyn Any + Sync + Send),
|
make: fn() -> Box<dyn Any + Sync + Send>,
|
||||||
) -> &'static (dyn Any + Sync + Send) {
|
) -> &'static (dyn Any + Sync + Send) {
|
||||||
let value = make();
|
let value = Box::leak(make());
|
||||||
let mut write_guard = self.0.write().unwrap();
|
let mut write_guard = self.0.write().unwrap();
|
||||||
*write_guard.entry(type_id).or_insert(value)
|
*write_guard.entry(type_id).or_insert(value)
|
||||||
}
|
}
|
||||||
pub(crate) fn get_or_insert_with<T: Sized + Any + Send + Sync>(
|
pub(crate) fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T {
|
||||||
&self,
|
|
||||||
make: impl FnOnce() -> &'static T,
|
|
||||||
) -> &'static T {
|
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
let read_guard = self.0.read().unwrap();
|
let read_guard = self.0.read().unwrap();
|
||||||
let retval = read_guard.get(&type_id).map(|v| *v);
|
let retval = read_guard.get(&type_id).map(|v| *v);
|
||||||
drop(read_guard);
|
drop(read_guard);
|
||||||
let retval = match retval {
|
let retval = match retval {
|
||||||
Some(retval) => retval,
|
Some(retval) => retval,
|
||||||
None => self.insert_slow(type_id, move || make()),
|
None => self.insert_slow(type_id, move || Box::new(T::default())),
|
||||||
};
|
};
|
||||||
retval.downcast_ref().expect("known to have correct TypeId")
|
retval.downcast_ref().expect("known to have correct TypeId")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,6 @@
|
||||||
// TODO: enable:
|
// TODO: enable:
|
||||||
// #![warn(missing_docs)]
|
// #![warn(missing_docs)]
|
||||||
|
|
||||||
#![deny(
|
|
||||||
rustdoc::bare_urls,
|
|
||||||
rustdoc::broken_intra_doc_links,
|
|
||||||
rustdoc::invalid_codeblock_attributes,
|
|
||||||
rustdoc::invalid_html_tags,
|
|
||||||
rustdoc::invalid_rust_codeblocks,
|
|
||||||
rustdoc::private_doc_tests,
|
|
||||||
rustdoc::private_intra_doc_links,
|
|
||||||
rustdoc::redundant_explicit_links,
|
|
||||||
rustdoc::unescaped_backticks
|
|
||||||
)]
|
|
||||||
|
|
||||||
//! [Main Documentation][_docs]
|
//! [Main Documentation][_docs]
|
||||||
|
|
||||||
extern crate self as fayalite;
|
extern crate self as fayalite;
|
||||||
|
|
@ -86,135 +74,6 @@ macro_rules! __cfg_expansion_helper {
|
||||||
pub use fayalite_proc_macros::hdl_module;
|
pub use fayalite_proc_macros::hdl_module;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
/// The `#[hdl]` attribute is supported on several different kinds of [Rust Items](https://doc.rust-lang.org/reference/items.html):
|
|
||||||
///
|
|
||||||
/// # Functions and Methods
|
|
||||||
/// Enable's the stuff that you can use inside a [module's body](crate::_docs::modules::module_bodies),
|
|
||||||
/// but without being a module or changing the function's signature.
|
|
||||||
/// The only exception is that you can't use stuff that requires the automatically-provided `m` variable.
|
|
||||||
///
|
|
||||||
/// # Structs
|
|
||||||
// TODO: expand on struct docs
|
|
||||||
/// e.g.:
|
|
||||||
/// ```
|
|
||||||
/// # use fayalite::prelude::*;
|
|
||||||
/// # #[hdl]
|
|
||||||
/// # pub struct OtherStruct {}
|
|
||||||
/// #[hdl]
|
|
||||||
/// pub struct MyStruct {
|
|
||||||
/// #[hdl(flip)]
|
|
||||||
/// pub a: UInt<5>,
|
|
||||||
/// pub b: Bool,
|
|
||||||
/// #[hdl(flip)]
|
|
||||||
/// pub c: OtherStruct,
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Enums
|
|
||||||
// TODO: expand on enum docs
|
|
||||||
/// e.g.:
|
|
||||||
/// ```
|
|
||||||
/// # use fayalite::prelude::*;
|
|
||||||
/// # #[hdl]
|
|
||||||
/// # pub struct MyStruct {}
|
|
||||||
/// #[hdl]
|
|
||||||
/// pub enum MyEnum {
|
|
||||||
/// A(UInt<3>),
|
|
||||||
/// B,
|
|
||||||
/// C(MyStruct),
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Type Aliases
|
|
||||||
///
|
|
||||||
/// There's three different ways you can create a type alias:
|
|
||||||
///
|
|
||||||
/// # Normal Type Alias
|
|
||||||
///
|
|
||||||
/// This works exactly how you'd expect:
|
|
||||||
/// ```
|
|
||||||
/// # use fayalite::prelude::*;
|
|
||||||
/// # #[hdl]
|
|
||||||
/// # pub struct MyStruct<T: Type> {
|
|
||||||
/// # v: T,
|
|
||||||
/// # }
|
|
||||||
/// #[hdl]
|
|
||||||
/// pub type MyType<T: Type> = MyStruct<T>;
|
|
||||||
///
|
|
||||||
/// // you can then use Fayalite's standard syntax for creating dynamic types at runtime:
|
|
||||||
///
|
|
||||||
/// let ty = MyType[UInt[3]];
|
|
||||||
/// assert_eq!(ty, MyStruct[UInt[3]]);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Type Alias that gets a [`Type`] from a [`PhantomConst`]
|
|
||||||
///
|
|
||||||
/// This allows you to use some computed property of a [`PhantomConst`] to get a [`Type`] that you can use in other #[hdl] types.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use fayalite::prelude::*;
|
|
||||||
/// #[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
|
|
||||||
/// pub struct Config {
|
|
||||||
/// pub foo: usize,
|
|
||||||
/// pub bar: Bundle,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // the expression inside `get` is called with `Interned<Config>` and returns `Array<Bundle>`
|
|
||||||
/// #[hdl(get(|config| Array[config.bar][config.foo]))]
|
|
||||||
/// pub type GetMyArray<P: PhantomConstGet<Config>> = Array<Bundle>;
|
|
||||||
///
|
|
||||||
/// // you can then use it in other types:
|
|
||||||
///
|
|
||||||
/// #[hdl(no_static)]
|
|
||||||
/// pub struct WrapMyArray<P: PhantomConstGet<Config>> {
|
|
||||||
/// pub my_array: GetMyArray<P>,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // you can then use Fayalite's standard syntax for creating dynamic types at runtime:
|
|
||||||
/// let bar = Bundle::new(Default::default());
|
|
||||||
/// let config = PhantomConst::new_sized(Config { foo: 12, bar });
|
|
||||||
/// let ty = WrapMyArray[config];
|
|
||||||
/// assert_eq!(ty.my_array, Array[bar][12]);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Type Alias that gets a [`Size`] from a [`PhantomConst`]
|
|
||||||
///
|
|
||||||
/// This allows you to use some computed property of a [`PhantomConst`] to get a [`Size`] that you can use in other #[hdl] types.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use fayalite::prelude::*;
|
|
||||||
/// # #[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
|
|
||||||
/// # pub struct ConfigItem {}
|
|
||||||
/// # impl ConfigItem {
|
|
||||||
/// # pub fn new() -> Self {
|
|
||||||
/// # Self {}
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// #[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
|
|
||||||
/// pub struct Config {
|
|
||||||
/// pub items: Vec<ConfigItem>,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // the expression inside `get` is called with `Interned<Config>` and returns `usize` (not DynSize)
|
|
||||||
/// #[hdl(get(|config| config.items.len()))]
|
|
||||||
/// pub type GetItemsLen<P: PhantomConstGet<Config>> = DynSize; // must be DynSize
|
|
||||||
///
|
|
||||||
/// // you can then use it in other types:
|
|
||||||
///
|
|
||||||
/// #[hdl(no_static)]
|
|
||||||
/// pub struct FlagPerItem<P: PhantomConstGet<Config>> {
|
|
||||||
/// pub flags: ArrayType<Bool, GetItemsLen<P>>,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // you can then use Fayalite's standard syntax for creating dynamic types at runtime:
|
|
||||||
/// let config = PhantomConst::new_sized(Config { items: vec![ConfigItem::new(); 5] });
|
|
||||||
/// let ty = FlagPerItem[config];
|
|
||||||
/// assert_eq!(ty.flags, Array[Bool][5]);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`PhantomConst`]: crate::phantom_const::PhantomConst
|
|
||||||
/// [`Size`]: crate::int::Size
|
|
||||||
/// [`Type`]: crate::ty::Type
|
|
||||||
pub use fayalite_proc_macros::hdl;
|
pub use fayalite_proc_macros::hdl;
|
||||||
|
|
||||||
pub use bitvec;
|
pub use bitvec;
|
||||||
|
|
@ -228,8 +87,8 @@ pub mod _docs;
|
||||||
|
|
||||||
pub mod annotations;
|
pub mod annotations;
|
||||||
pub mod array;
|
pub mod array;
|
||||||
pub mod build;
|
|
||||||
pub mod bundle;
|
pub mod bundle;
|
||||||
|
pub mod cli;
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod enum_;
|
pub mod enum_;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
|
|
@ -240,7 +99,6 @@ pub mod intern;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod phantom_const;
|
pub mod phantom_const;
|
||||||
pub mod platform;
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod reg;
|
pub mod reg;
|
||||||
pub mod reset;
|
pub mod reset;
|
||||||
|
|
@ -249,5 +107,4 @@ pub mod source_location;
|
||||||
pub mod testing;
|
pub mod testing;
|
||||||
pub mod ty;
|
pub mod ty;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod vendor;
|
|
||||||
pub mod wire;
|
pub mod wire;
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,7 @@ use crate::{
|
||||||
array::{Array, ArrayType},
|
array::{Array, ArrayType},
|
||||||
bundle::{Bundle, BundleType},
|
bundle::{Bundle, BundleType},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
expr::{
|
expr::{Expr, Flow, ToExpr, ToLiteralBits, ops::BundleLiteral, repeat},
|
||||||
Expr, Flow, ToExpr, ToLiteralBits, ValueType, ops::BundleLiteral, repeat,
|
|
||||||
value_category::ValueCategoryExpr,
|
|
||||||
},
|
|
||||||
hdl,
|
hdl,
|
||||||
int::{Bool, DynSize, Size, UInt, UIntType},
|
int::{Bool, DynSize, Size, UInt, UIntType},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
|
|
@ -369,16 +366,10 @@ impl<T: PortType> fmt::Debug for MemPort<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PortType> ValueType for MemPort<T> {
|
impl<T: PortType> MemPort<T> {
|
||||||
type Type = T::Port;
|
pub fn ty(&self) -> T::Port {
|
||||||
type ValueCategory = ValueCategoryExpr;
|
|
||||||
|
|
||||||
fn ty(&self) -> T::Port {
|
|
||||||
T::port_ty(self)
|
T::port_ty(self)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PortType> MemPort<T> {
|
|
||||||
pub fn source_location(&self) -> SourceLocation {
|
pub fn source_location(&self) -> SourceLocation {
|
||||||
self.source_location
|
self.source_location
|
||||||
}
|
}
|
||||||
|
|
@ -839,7 +830,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
||||||
depth: Option<usize>,
|
depth: Option<usize>,
|
||||||
initial_value: Expr<Array>,
|
initial_value: Expr<Array>,
|
||||||
) -> Interned<BitSlice> {
|
) -> Interned<BitSlice> {
|
||||||
let initial_value_ty = initial_value.ty();
|
let initial_value_ty = Expr::ty(initial_value);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*mem_element_type,
|
*mem_element_type,
|
||||||
Element::from_canonical(initial_value_ty.element()),
|
Element::from_canonical(initial_value_ty.element()),
|
||||||
|
|
@ -1020,7 +1011,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
||||||
target.depth,
|
target.depth,
|
||||||
initial_value,
|
initial_value,
|
||||||
));
|
));
|
||||||
target.depth = Some(initial_value.ty().len());
|
target.depth = Some(Expr::ty(initial_value).len());
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {
|
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {
|
||||||
|
|
@ -1093,7 +1084,6 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
|
||||||
.to_expr(),
|
.to_expr(),
|
||||||
)),
|
)),
|
||||||
CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())),
|
CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())),
|
||||||
CanonicalType::TraceAsString(ty) => Expr::from_canonical(splat_mask(ty.inner_ty(), value)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,6 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
pub mod deduce_resets;
|
pub mod deduce_resets;
|
||||||
pub mod deduce_structural_eq_flags;
|
|
||||||
pub mod simplify_enums;
|
pub mod simplify_enums;
|
||||||
pub mod simplify_memories;
|
pub mod simplify_memories;
|
||||||
pub mod visit;
|
pub mod visit;
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,11 @@ use crate::{
|
||||||
bundle::{BundleField, BundleType},
|
bundle::{BundleField, BundleType},
|
||||||
enum_::{EnumType, EnumVariant},
|
enum_::{EnumType, EnumVariant},
|
||||||
expr::{
|
expr::{
|
||||||
ExprEnum, ValueType,
|
ExprEnum,
|
||||||
ops::{self, ArrayLiteral},
|
ops::{self, ArrayLiteral},
|
||||||
target::{
|
target::{
|
||||||
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
|
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
|
||||||
TargetPathDynArrayElement, TargetPathElement, TargetPathToTraceAsString,
|
TargetPathDynArrayElement, TargetPathElement,
|
||||||
TargetPathTraceAsStringInner,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formal::FormalKind,
|
formal::FormalKind,
|
||||||
|
|
@ -27,7 +26,6 @@ use crate::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
reset::{ResetType, ResetTypeDispatch},
|
reset::{ResetType, ResetTypeDispatch},
|
||||||
sim::ExternModuleSimulation,
|
sim::ExternModuleSimulation,
|
||||||
ty::TraceAsString,
|
|
||||||
util::{HashMap, HashSet},
|
util::{HashMap, HashSet},
|
||||||
};
|
};
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
|
|
@ -105,10 +103,6 @@ enum ResetsLayout {
|
||||||
element: Interned<ResetsLayout>,
|
element: Interned<ResetsLayout>,
|
||||||
reset_count: usize,
|
reset_count: usize,
|
||||||
},
|
},
|
||||||
Transparent {
|
|
||||||
inner: Interned<ResetsLayout>,
|
|
||||||
reset_count: usize,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResetsLayout {
|
impl ResetsLayout {
|
||||||
|
|
@ -118,8 +112,7 @@ impl ResetsLayout {
|
||||||
ResetsLayout::Reset | ResetsLayout::SyncReset | ResetsLayout::AsyncReset => 1,
|
ResetsLayout::Reset | ResetsLayout::SyncReset | ResetsLayout::AsyncReset => 1,
|
||||||
ResetsLayout::Bundle { reset_count, .. }
|
ResetsLayout::Bundle { reset_count, .. }
|
||||||
| ResetsLayout::Enum { reset_count, .. }
|
| ResetsLayout::Enum { reset_count, .. }
|
||||||
| ResetsLayout::Array { reset_count, .. }
|
| ResetsLayout::Array { reset_count, .. } => reset_count,
|
||||||
| ResetsLayout::Transparent { reset_count, .. } => reset_count,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn new(ty: CanonicalType) -> Self {
|
fn new(ty: CanonicalType) -> Self {
|
||||||
|
|
@ -173,13 +166,6 @@ impl ResetsLayout {
|
||||||
CanonicalType::Clock(_) => ResetsLayout::NoResets,
|
CanonicalType::Clock(_) => ResetsLayout::NoResets,
|
||||||
CanonicalType::PhantomConst(_) => ResetsLayout::NoResets,
|
CanonicalType::PhantomConst(_) => ResetsLayout::NoResets,
|
||||||
CanonicalType::DynSimOnly(_) => ResetsLayout::NoResets,
|
CanonicalType::DynSimOnly(_) => ResetsLayout::NoResets,
|
||||||
CanonicalType::TraceAsString(ty) => {
|
|
||||||
let inner = ResetsLayout::new(ty.inner_ty()).intern_sized();
|
|
||||||
ResetsLayout::Transparent {
|
|
||||||
inner,
|
|
||||||
reset_count: inner.reset_count(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -329,12 +315,6 @@ impl ResetGraph {
|
||||||
} => {
|
} => {
|
||||||
self.append_new_nodes_for_layout(*element, node_indexes, source_location);
|
self.append_new_nodes_for_layout(*element, node_indexes, source_location);
|
||||||
}
|
}
|
||||||
ResetsLayout::Transparent {
|
|
||||||
inner,
|
|
||||||
reset_count: _,
|
|
||||||
} => {
|
|
||||||
self.append_new_nodes_for_layout(*inner, node_indexes, source_location);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -377,21 +357,6 @@ impl Resets {
|
||||||
node_indexes: self.node_indexes,
|
node_indexes: self.node_indexes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn trace_as_string_inner(self) -> Self {
|
|
||||||
let trace_as_string = TraceAsString::from_canonical(self.ty);
|
|
||||||
let ResetsLayout::Transparent {
|
|
||||||
inner,
|
|
||||||
reset_count: _,
|
|
||||||
} = self.layout
|
|
||||||
else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
ty: trace_as_string.inner_ty(),
|
|
||||||
layout: *inner,
|
|
||||||
node_indexes: self.node_indexes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn bundle_fields(self) -> impl Iterator<Item = Self> {
|
fn bundle_fields(self) -> impl Iterator<Item = Self> {
|
||||||
let bundle = Bundle::from_canonical(self.ty);
|
let bundle = Bundle::from_canonical(self.ty);
|
||||||
let ResetsLayout::Bundle {
|
let ResetsLayout::Bundle {
|
||||||
|
|
@ -515,17 +480,6 @@ impl Resets {
|
||||||
CanonicalType::SyncReset(SyncReset)
|
CanonicalType::SyncReset(SyncReset)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
CanonicalType::TraceAsString(ty) => Ok(CanonicalType::TraceAsString(
|
|
||||||
ty.with_new_inner_ty(
|
|
||||||
self.array_elements()
|
|
||||||
.substituted_type(
|
|
||||||
reset_graph,
|
|
||||||
fallback_to_sync_reset,
|
|
||||||
fallback_error_source_location,
|
|
||||||
)?
|
|
||||||
.intern_sized(),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -567,7 +521,7 @@ impl State {
|
||||||
Entry::Vacant(entry) => (
|
Entry::Vacant(entry) => (
|
||||||
*entry.insert(Resets::with_new_nodes(
|
*entry.insert(Resets::with_new_nodes(
|
||||||
&mut self.reset_graph,
|
&mut self.reset_graph,
|
||||||
expr.ty(),
|
Expr::ty(expr),
|
||||||
source_location,
|
source_location,
|
||||||
)),
|
)),
|
||||||
true,
|
true,
|
||||||
|
|
@ -1059,20 +1013,18 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
|
||||||
| CanonicalType::Bundle(_)
|
| CanonicalType::Bundle(_)
|
||||||
| CanonicalType::Reset(_)
|
| CanonicalType::Reset(_)
|
||||||
| CanonicalType::PhantomConst(_)
|
| CanonicalType::PhantomConst(_)
|
||||||
| CanonicalType::DynSimOnly(_)
|
| CanonicalType::DynSimOnly(_) => unreachable!(),
|
||||||
| CanonicalType::TraceAsString(_) => unreachable!(),
|
|
||||||
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
|
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
macro_rules! match_arg_ty {
|
macro_rules! match_arg_ty {
|
||||||
($($Variant:ident),*) => {
|
($($Variant:ident),*) => {
|
||||||
*match arg.ty() {
|
*match Expr::ty(arg) {
|
||||||
CanonicalType::Array(_)
|
CanonicalType::Array(_)
|
||||||
| CanonicalType::Enum(_)
|
| CanonicalType::Enum(_)
|
||||||
| CanonicalType::Bundle(_)
|
| CanonicalType::Bundle(_)
|
||||||
| CanonicalType::Reset(_)
|
| CanonicalType::Reset(_) => unreachable!(),
|
||||||
| CanonicalType::TraceAsString(_) => unreachable!(),
|
|
||||||
CanonicalType::PhantomConst(_) |
|
CanonicalType::PhantomConst(_) |
|
||||||
CanonicalType::DynSimOnly(_) => Expr::expr_enum(arg),
|
CanonicalType::DynSimOnly(_) => Expr::expr_enum(arg),
|
||||||
$(CanonicalType::$Variant(_) => {
|
$(CanonicalType::$Variant(_) => {
|
||||||
|
|
@ -1204,11 +1156,6 @@ impl<P: Pass> RunPass<P> for ExprEnum {
|
||||||
ExprEnum::SliceSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::SliceSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::CastToBits(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::CastToBits(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::CastBitsTo(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::CastBitsTo(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::TraceAsStringAsInner(expr) => {
|
|
||||||
Ok(expr.run_pass(pass_args)?.map(ExprEnum::from))
|
|
||||||
}
|
|
||||||
ExprEnum::ToTraceAsString(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
|
||||||
ExprEnum::StructuralEq(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
|
||||||
ExprEnum::ModuleIO(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::ModuleIO(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::Instance(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::Instance(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::Wire(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::Wire(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
|
|
@ -1216,10 +1163,6 @@ impl<P: Pass> RunPass<P> for ExprEnum {
|
||||||
ExprEnum::RegSync(expr) => reg_expr_run_pass(expr, pass_args),
|
ExprEnum::RegSync(expr) => reg_expr_run_pass(expr, pass_args),
|
||||||
ExprEnum::RegAsync(expr) => reg_expr_run_pass(expr, pass_args),
|
ExprEnum::RegAsync(expr) => reg_expr_run_pass(expr, pass_args),
|
||||||
ExprEnum::MemPort(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::MemPort(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::FormalInput(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
|
||||||
ExprEnum::SimIoForGlobal(_) => {
|
|
||||||
unreachable!("Module is known to not contain SimIoForGlobal from validation")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1593,96 +1536,6 @@ impl RunPassExpr for ops::CastBitsTo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunPassExpr for ops::TraceAsStringAsInner {
|
|
||||||
type Args<'a> = [Expr<CanonicalType>; 1];
|
|
||||||
|
|
||||||
fn args<'a>(&'a self) -> Self::Args<'a> {
|
|
||||||
[Expr::canonical(self.arg())]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source_location(&self) -> Option<SourceLocation> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn union_parts(
|
|
||||||
&self,
|
|
||||||
resets: Resets,
|
|
||||||
args_resets: Vec<Resets>,
|
|
||||||
mut pass_args: PassArgs<'_, BuildResetGraph>,
|
|
||||||
) -> Result<(), DeduceResetsError> {
|
|
||||||
pass_args.union(resets, args_resets[0].trace_as_string_inner(), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(
|
|
||||||
&self,
|
|
||||||
_ty: CanonicalType,
|
|
||||||
new_args: Vec<Expr<CanonicalType>>,
|
|
||||||
) -> Result<Self, DeduceResetsError> {
|
|
||||||
Ok(Self::new(Expr::from_canonical(new_args[0])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RunPassExpr for ops::ToTraceAsString {
|
|
||||||
type Args<'a> = [Expr<CanonicalType>; 1];
|
|
||||||
|
|
||||||
fn args<'a>(&'a self) -> Self::Args<'a> {
|
|
||||||
[Expr::canonical(self.inner())]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source_location(&self) -> Option<SourceLocation> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn union_parts(
|
|
||||||
&self,
|
|
||||||
resets: Resets,
|
|
||||||
args_resets: Vec<Resets>,
|
|
||||||
mut pass_args: PassArgs<'_, BuildResetGraph>,
|
|
||||||
) -> Result<(), DeduceResetsError> {
|
|
||||||
pass_args.union(resets.trace_as_string_inner(), args_resets[0], None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(
|
|
||||||
&self,
|
|
||||||
_ty: CanonicalType,
|
|
||||||
new_args: Vec<Expr<CanonicalType>>,
|
|
||||||
) -> Result<Self, DeduceResetsError> {
|
|
||||||
Ok(Self::new(
|
|
||||||
new_args[0],
|
|
||||||
self.ty().with_new_inner_ty(new_args[0].ty().intern_sized()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RunPassExpr for ops::StructuralEq {
|
|
||||||
type Args<'a> = [Expr<CanonicalType>; 2];
|
|
||||||
|
|
||||||
fn args<'a>(&'a self) -> Self::Args<'a> {
|
|
||||||
[self.lhs(), self.rhs()]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source_location(&self) -> Option<SourceLocation> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn union_parts(
|
|
||||||
&self,
|
|
||||||
_resets: Resets,
|
|
||||||
args_resets: Vec<Resets>,
|
|
||||||
mut pass_args: PassArgs<'_, BuildResetGraph>,
|
|
||||||
) -> Result<(), DeduceResetsError> {
|
|
||||||
pass_args.union(args_resets[0], args_resets[1], None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(
|
|
||||||
&self,
|
|
||||||
_ty: CanonicalType,
|
|
||||||
new_args: Vec<Expr<CanonicalType>>,
|
|
||||||
) -> Result<Self, DeduceResetsError> {
|
|
||||||
Ok(Self::with_flags(new_args[0], new_args[1], self.flags()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RunPassExpr for ModuleIO<CanonicalType> {
|
impl RunPassExpr for ModuleIO<CanonicalType> {
|
||||||
type Args<'a> = [Expr<CanonicalType>; 0];
|
type Args<'a> = [Expr<CanonicalType>; 0];
|
||||||
|
|
||||||
|
|
@ -1807,8 +1660,7 @@ impl RunPassDispatch for AnyReg {
|
||||||
let clock_domain = Expr::<Bundle>::from_canonical(
|
let clock_domain = Expr::<Bundle>::from_canonical(
|
||||||
Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0,
|
Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0,
|
||||||
);
|
);
|
||||||
match clock_domain
|
match Expr::ty(clock_domain)
|
||||||
.ty()
|
|
||||||
.field_by_name("rst".intern())
|
.field_by_name("rst".intern())
|
||||||
.expect("ClockDomain has rst field")
|
.expect("ClockDomain has rst field")
|
||||||
.ty
|
.ty
|
||||||
|
|
@ -1838,8 +1690,7 @@ impl RunPassDispatch for AnyReg {
|
||||||
| CanonicalType::Reset(_)
|
| CanonicalType::Reset(_)
|
||||||
| CanonicalType::Clock(_)
|
| CanonicalType::Clock(_)
|
||||||
| CanonicalType::PhantomConst(_)
|
| CanonicalType::PhantomConst(_)
|
||||||
| CanonicalType::DynSimOnly(_)
|
| CanonicalType::DynSimOnly(_) => unreachable!(),
|
||||||
| CanonicalType::TraceAsString(_) => unreachable!(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -1951,7 +1802,6 @@ impl_run_pass_clone!([] ExternModuleParameter);
|
||||||
impl_run_pass_clone!([] SIntValue);
|
impl_run_pass_clone!([] SIntValue);
|
||||||
impl_run_pass_clone!([] std::ops::Range<usize>);
|
impl_run_pass_clone!([] std::ops::Range<usize>);
|
||||||
impl_run_pass_clone!([] UIntValue);
|
impl_run_pass_clone!([] UIntValue);
|
||||||
impl_run_pass_clone!([] crate::vendor::xilinx::XilinxAnnotation);
|
|
||||||
impl_run_pass_copy!([] BlackBoxInlineAnnotation);
|
impl_run_pass_copy!([] BlackBoxInlineAnnotation);
|
||||||
impl_run_pass_copy!([] BlackBoxPathAnnotation);
|
impl_run_pass_copy!([] BlackBoxPathAnnotation);
|
||||||
impl_run_pass_copy!([] bool);
|
impl_run_pass_copy!([] bool);
|
||||||
|
|
@ -1966,7 +1816,6 @@ impl_run_pass_copy!([] SVAttributeAnnotation);
|
||||||
impl_run_pass_copy!([] UInt);
|
impl_run_pass_copy!([] UInt);
|
||||||
impl_run_pass_copy!([] usize);
|
impl_run_pass_copy!([] usize);
|
||||||
impl_run_pass_copy!([] FormalKind);
|
impl_run_pass_copy!([] FormalKind);
|
||||||
impl_run_pass_copy!([] crate::formal::FormalInput);
|
|
||||||
impl_run_pass_copy!([] PhantomConst);
|
impl_run_pass_copy!([] PhantomConst);
|
||||||
|
|
||||||
macro_rules! impl_run_pass_for_struct {
|
macro_rules! impl_run_pass_for_struct {
|
||||||
|
|
@ -2223,7 +2072,6 @@ impl_run_pass_for_struct! {
|
||||||
impl[] RunPass for ExternModuleBody {
|
impl[] RunPass for ExternModuleBody {
|
||||||
verilog_name: _,
|
verilog_name: _,
|
||||||
parameters: _,
|
parameters: _,
|
||||||
clocks_for_past: _,
|
|
||||||
simulation: _,
|
simulation: _,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2283,12 +2131,6 @@ impl<P: Pass> RunPass<P> for TargetBase {
|
||||||
&TargetBase::RegAsync(v) => v.into(),
|
&TargetBase::RegAsync(v) => v.into(),
|
||||||
TargetBase::Wire(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Wire)),
|
TargetBase::Wire(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Wire)),
|
||||||
TargetBase::Instance(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Instance)),
|
TargetBase::Instance(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Instance)),
|
||||||
TargetBase::FormalInput(v) => {
|
|
||||||
return Ok(v.run_pass(pass_args)?.map(TargetBase::FormalInput));
|
|
||||||
}
|
|
||||||
TargetBase::SimIoForGlobal(_) => {
|
|
||||||
unreachable!("Module is known to not contain SimIoForGlobal from validation")
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(reg.run_pass(pass_args)?.map(|reg| match reg {
|
Ok(reg.run_pass(pass_args)?.map(|reg| match reg {
|
||||||
AnyReg::Reg(reg) => TargetBase::Reg(reg),
|
AnyReg::Reg(reg) => TargetBase::Reg(reg),
|
||||||
|
|
@ -2328,6 +2170,30 @@ impl<P: Pass> RunPass<P> for StmtDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_run_pass_for_struct! {
|
||||||
|
impl[] RunPass for TargetPathBundleField {
|
||||||
|
name: _,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_run_pass_for_struct! {
|
||||||
|
impl[] RunPass for TargetPathArrayElement {
|
||||||
|
index: _,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_run_pass_for_struct! {
|
||||||
|
impl[] RunPass for TargetPathDynArrayElement {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_run_pass_for_enum! {
|
||||||
|
impl[] RunPass for TargetPathElement {
|
||||||
|
BundleField(v),
|
||||||
|
ArrayElement(v),
|
||||||
|
DynArrayElement(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl_run_pass_for_enum! {
|
impl_run_pass_for_enum! {
|
||||||
impl[] RunPass for Target {
|
impl[] RunPass for Target {
|
||||||
Base(v),
|
Base(v),
|
||||||
|
|
@ -2335,28 +2201,11 @@ impl_run_pass_for_enum! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Pass> RunPass<P> for TargetChild {
|
impl_run_pass_for_struct! {
|
||||||
fn run_pass(
|
#[constructor = TargetChild::new(parent, path_element)]
|
||||||
&self,
|
impl[] RunPass for TargetChild {
|
||||||
mut pass_args: PassArgs<'_, P>,
|
parent(): _,
|
||||||
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
|
path_element(): _,
|
||||||
Ok(self.parent().run_pass(pass_args.as_mut())?.map(|parent| {
|
|
||||||
let path_element = match *self.path_element() {
|
|
||||||
TargetPathElement::BundleField(TargetPathBundleField { name: _ })
|
|
||||||
| TargetPathElement::ArrayElement(TargetPathArrayElement { index: _ })
|
|
||||||
| TargetPathElement::DynArrayElement(TargetPathDynArrayElement {})
|
|
||||||
| TargetPathElement::TraceAsStringInner(TargetPathTraceAsStringInner {}) => {
|
|
||||||
self.path_element()
|
|
||||||
}
|
|
||||||
TargetPathElement::ToTraceAsString(TargetPathToTraceAsString { ty }) => {
|
|
||||||
TargetPathElement::from(TargetPathToTraceAsString {
|
|
||||||
ty: ty.with_new_inner_ty(parent.canonical_ty().intern_sized()),
|
|
||||||
})
|
|
||||||
.intern_sized()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
TargetChild::new(parent, path_element)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2368,7 +2217,6 @@ impl_run_pass_for_enum! {
|
||||||
BlackBoxPath(v),
|
BlackBoxPath(v),
|
||||||
DocString(v),
|
DocString(v),
|
||||||
CustomFirrtl(v),
|
CustomFirrtl(v),
|
||||||
Xilinx(v),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,26 +1,27 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::{BundleField, BundleType},
|
array::{Array, ArrayType},
|
||||||
enum_::{EnumType, EnumVariant},
|
bundle::{Bundle, BundleField, BundleType},
|
||||||
|
enum_::{Enum, EnumType, EnumVariant},
|
||||||
expr::{
|
expr::{
|
||||||
ExprEnum,
|
CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr,
|
||||||
ops::{self, EnumLiteral, StructuralEq, StructuralEqFlags},
|
ops::{self, EnumLiteral},
|
||||||
},
|
},
|
||||||
intern::{Intern, InternSlice, Interned, Memoize},
|
hdl,
|
||||||
memory::{DynPortType, MemPort},
|
int::UInt,
|
||||||
|
intern::{Intern, Interned, Memoize},
|
||||||
|
memory::{DynPortType, Mem, MemPort},
|
||||||
module::{
|
module::{
|
||||||
Block, Id, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
|
Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
|
||||||
transform::{
|
transform::visit::{Fold, Folder},
|
||||||
deduce_structural_eq_flags::deduce_structural_eq_flags,
|
|
||||||
visit::{Fold, Folder},
|
|
||||||
},
|
},
|
||||||
},
|
source_location::SourceLocation,
|
||||||
prelude::*,
|
ty::{CanonicalType, Type},
|
||||||
util::HashMap,
|
util::HashMap,
|
||||||
|
wire::Wire,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use core::fmt;
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SimplifyEnumsError {
|
pub enum SimplifyEnumsError {
|
||||||
|
|
@ -62,7 +63,6 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool {
|
||||||
.fields()
|
.fields()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|field| contains_any_enum_types(field.ty)),
|
.any(|field| contains_any_enum_types(field.ty)),
|
||||||
CanonicalType::TraceAsString(ty) => contains_any_enum_types(ty.inner_ty()),
|
|
||||||
CanonicalType::UInt(_)
|
CanonicalType::UInt(_)
|
||||||
| CanonicalType::SInt(_)
|
| CanonicalType::SInt(_)
|
||||||
| CanonicalType::Bool(_)
|
| CanonicalType::Bool(_)
|
||||||
|
|
@ -94,13 +94,11 @@ enum EnumTypeState {
|
||||||
|
|
||||||
struct ModuleState {
|
struct ModuleState {
|
||||||
module_name: NameId,
|
module_name: NameId,
|
||||||
expr_cache: HashMap<ExprEnum, ExprEnum>,
|
|
||||||
source_location: SourceLocation,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleState {
|
impl ModuleState {
|
||||||
fn gen_name(&mut self, name: &str) -> ScopedNameId {
|
fn gen_name(&mut self, name: &str) -> ScopedNameId {
|
||||||
ScopedNameId(self.module_name.into(), NameId(name.intern(), Id::new()))
|
ScopedNameId(self.module_name, NameId(name.intern(), Id::new()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,45 +107,6 @@ struct State {
|
||||||
replacement_mem_ports: HashMap<MemPort<DynPortType>, Wire<CanonicalType>>,
|
replacement_mem_ports: HashMap<MemPort<DynPortType>, Wire<CanonicalType>>,
|
||||||
kind: SimplifyEnumsKind,
|
kind: SimplifyEnumsKind,
|
||||||
module_state_stack: Vec<ModuleState>,
|
module_state_stack: Vec<ModuleState>,
|
||||||
new_prefix_stmts_for_block: Vec<Stmt>,
|
|
||||||
new_suffix_stmts_for_block: Vec<Stmt>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BlockScope<'a> {
|
|
||||||
state: &'a mut State,
|
|
||||||
parent_new_prefix_stmts_for_block: Vec<Stmt>,
|
|
||||||
parent_new_suffix_stmts_for_block: Vec<Stmt>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockScope<'a> {
|
|
||||||
fn new(
|
|
||||||
state: &'a mut State,
|
|
||||||
new_prefix_stmts_for_block: Vec<Stmt>,
|
|
||||||
new_suffix_stmts_for_block: Vec<Stmt>,
|
|
||||||
) -> Self {
|
|
||||||
let parent_new_prefix_stmts_for_block = std::mem::replace(
|
|
||||||
&mut state.new_prefix_stmts_for_block,
|
|
||||||
new_prefix_stmts_for_block,
|
|
||||||
);
|
|
||||||
let parent_new_suffix_stmts_for_block = std::mem::replace(
|
|
||||||
&mut state.new_suffix_stmts_for_block,
|
|
||||||
new_suffix_stmts_for_block,
|
|
||||||
);
|
|
||||||
Self {
|
|
||||||
state,
|
|
||||||
parent_new_prefix_stmts_for_block,
|
|
||||||
parent_new_suffix_stmts_for_block,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for BlockScope<'_> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.state.new_prefix_stmts_for_block =
|
|
||||||
std::mem::take(&mut self.parent_new_prefix_stmts_for_block);
|
|
||||||
self.state.new_suffix_stmts_for_block =
|
|
||||||
std::mem::take(&mut self.parent_new_suffix_stmts_for_block);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
|
@ -353,24 +312,6 @@ impl State {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn handle_stmt_connect_trace_as_string(
|
|
||||||
&mut self,
|
|
||||||
unfolded_lhs_ty: TraceAsString,
|
|
||||||
unfolded_rhs_ty: TraceAsString,
|
|
||||||
folded_lhs: Expr<TraceAsString>,
|
|
||||||
folded_rhs: Expr<TraceAsString>,
|
|
||||||
source_location: SourceLocation,
|
|
||||||
output_stmts: &mut Vec<Stmt>,
|
|
||||||
) -> Result<(), SimplifyEnumsError> {
|
|
||||||
self.handle_stmt_connect(
|
|
||||||
unfolded_lhs_ty.inner_ty(),
|
|
||||||
unfolded_rhs_ty.inner_ty(),
|
|
||||||
ops::TraceAsStringAsInner::new(folded_lhs).to_expr(),
|
|
||||||
ops::TraceAsStringAsInner::new(folded_rhs).to_expr(),
|
|
||||||
source_location,
|
|
||||||
output_stmts,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
fn handle_stmt_connect_bundle(
|
fn handle_stmt_connect_bundle(
|
||||||
&mut self,
|
&mut self,
|
||||||
unfolded_lhs_ty: Bundle,
|
unfolded_lhs_ty: Bundle,
|
||||||
|
|
@ -567,15 +508,6 @@ impl State {
|
||||||
source_location,
|
source_location,
|
||||||
output_stmts,
|
output_stmts,
|
||||||
),
|
),
|
||||||
CanonicalType::TraceAsString(unfolded_lhs_ty) => self
|
|
||||||
.handle_stmt_connect_trace_as_string(
|
|
||||||
unfolded_lhs_ty,
|
|
||||||
TraceAsString::from_canonical(unfolded_rhs_ty),
|
|
||||||
Expr::from_canonical(folded_lhs),
|
|
||||||
Expr::from_canonical(folded_rhs),
|
|
||||||
source_location,
|
|
||||||
output_stmts,
|
|
||||||
),
|
|
||||||
CanonicalType::UInt(_)
|
CanonicalType::UInt(_)
|
||||||
| CanonicalType::SInt(_)
|
| CanonicalType::SInt(_)
|
||||||
| CanonicalType::Bool(_)
|
| CanonicalType::Bool(_)
|
||||||
|
|
@ -587,185 +519,6 @@ impl State {
|
||||||
| CanonicalType::DynSimOnly(_) => unreachable!(),
|
| CanonicalType::DynSimOnly(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn handle_enum_structural_eq(
|
|
||||||
&mut self,
|
|
||||||
unfolded_ty: Enum,
|
|
||||||
folded_lhs: Expr<CanonicalType>,
|
|
||||||
folded_rhs: Expr<CanonicalType>,
|
|
||||||
flags: StructuralEqFlags,
|
|
||||||
) -> Result<Expr<Bool>, SimplifyEnumsError> {
|
|
||||||
if flags.assume_padding_is_zeroed {
|
|
||||||
return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr());
|
|
||||||
}
|
|
||||||
let enum_type_state = self.get_or_make_enum_type_state(unfolded_ty)?;
|
|
||||||
if let EnumTypeState::Unchanged = enum_type_state {
|
|
||||||
return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr());
|
|
||||||
}
|
|
||||||
let module_state = self.module_state_stack.last_mut().unwrap();
|
|
||||||
let source_location = module_state.source_location;
|
|
||||||
let output_wire = Wire::new_unchecked(
|
|
||||||
module_state.gen_name("__enum_structural_eq"),
|
|
||||||
source_location,
|
|
||||||
Bool,
|
|
||||||
);
|
|
||||||
self.new_prefix_stmts_for_block.push(
|
|
||||||
StmtWire {
|
|
||||||
annotations: Interned::default(),
|
|
||||||
wire: output_wire.canonical(),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
let output_wire = output_wire.to_expr();
|
|
||||||
self.new_suffix_stmts_for_block.push(
|
|
||||||
StmtConnect {
|
|
||||||
lhs: Expr::canonical(output_wire),
|
|
||||||
rhs: Expr::canonical(false.to_expr()),
|
|
||||||
source_location,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
let tags_eq = match enum_type_state {
|
|
||||||
EnumTypeState::TagEnumAndBody(_) => StructuralEq::with_flags(
|
|
||||||
Expr::canonical(Expr::<TagAndBody<Enum, UInt>>::from_canonical(folded_lhs).tag),
|
|
||||||
Expr::canonical(Expr::<TagAndBody<Enum, UInt>>::from_canonical(folded_rhs).tag),
|
|
||||||
StructuralEqFlags {
|
|
||||||
assume_padding_is_zeroed: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.to_expr(),
|
|
||||||
EnumTypeState::TagUIntAndBody(_) => {
|
|
||||||
let lhs = Expr::<TagAndBody<UInt, UInt>>::from_canonical(folded_lhs).tag;
|
|
||||||
let rhs = Expr::<TagAndBody<UInt, UInt>>::from_canonical(folded_rhs).tag;
|
|
||||||
lhs.cmp_eq(rhs)
|
|
||||||
}
|
|
||||||
EnumTypeState::UInt(_) => {
|
|
||||||
let lhs_int_tag_expr = Expr::<UInt>::from_canonical(folded_lhs)
|
|
||||||
[..unfolded_ty.discriminant_bit_width()];
|
|
||||||
let rhs_int_tag_expr = Expr::<UInt>::from_canonical(folded_rhs)
|
|
||||||
[..unfolded_ty.discriminant_bit_width()];
|
|
||||||
lhs_int_tag_expr.cmp_eq(rhs_int_tag_expr)
|
|
||||||
}
|
|
||||||
EnumTypeState::Unchanged => unreachable!(),
|
|
||||||
};
|
|
||||||
let mut match_arms = Vec::with_capacity(unfolded_ty.variants().len());
|
|
||||||
for (variant_index, variant) in unfolded_ty.variants().iter().enumerate() {
|
|
||||||
let block_scope = BlockScope::new(self, vec![], vec![]);
|
|
||||||
let this = &mut *block_scope.state;
|
|
||||||
let eq = if let Some(variant_ty) = variant.ty {
|
|
||||||
let folded_lhs =
|
|
||||||
this.handle_variant_access(unfolded_ty, folded_lhs, variant_index)?;
|
|
||||||
let folded_rhs =
|
|
||||||
this.handle_variant_access(unfolded_ty, folded_rhs, variant_index)?;
|
|
||||||
this.handle_structural_eq(variant_ty, folded_lhs, folded_rhs, flags)?
|
|
||||||
} else {
|
|
||||||
true.to_expr()
|
|
||||||
};
|
|
||||||
match_arms.push(Block {
|
|
||||||
memories: [].intern_slice(),
|
|
||||||
stmts: this
|
|
||||||
.new_prefix_stmts_for_block
|
|
||||||
.drain(..)
|
|
||||||
.chain([StmtConnect {
|
|
||||||
lhs: Expr::canonical(output_wire),
|
|
||||||
rhs: Expr::canonical(eq),
|
|
||||||
source_location,
|
|
||||||
}
|
|
||||||
.into()])
|
|
||||||
.chain(this.new_suffix_stmts_for_block.drain(..))
|
|
||||||
.collect(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let match_stmt =
|
|
||||||
self.handle_match(unfolded_ty, folded_lhs, source_location, &match_arms)?;
|
|
||||||
self.new_suffix_stmts_for_block.push(
|
|
||||||
StmtIf {
|
|
||||||
cond: tags_eq,
|
|
||||||
source_location,
|
|
||||||
blocks: [
|
|
||||||
Block {
|
|
||||||
memories: [].intern_slice(),
|
|
||||||
stmts: [match_stmt].intern_slice(),
|
|
||||||
},
|
|
||||||
Block {
|
|
||||||
memories: [].intern_slice(),
|
|
||||||
stmts: [].intern_slice(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
Ok(output_wire)
|
|
||||||
}
|
|
||||||
fn handle_structural_eq(
|
|
||||||
&mut self,
|
|
||||||
unfolded_ty: CanonicalType,
|
|
||||||
folded_lhs: Expr<CanonicalType>,
|
|
||||||
folded_rhs: Expr<CanonicalType>,
|
|
||||||
flags: StructuralEqFlags,
|
|
||||||
) -> Result<Expr<Bool>, SimplifyEnumsError> {
|
|
||||||
if !contains_any_enum_types(unfolded_ty) {
|
|
||||||
return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr());
|
|
||||||
}
|
|
||||||
match unfolded_ty {
|
|
||||||
CanonicalType::Array(unfolded_ty) => {
|
|
||||||
let unfolded_element_ty = unfolded_ty.element();
|
|
||||||
let mut retval = None;
|
|
||||||
for i in 0..unfolded_ty.len() {
|
|
||||||
let element_eq = self.handle_structural_eq(
|
|
||||||
unfolded_element_ty,
|
|
||||||
ops::ArrayIndex::new(Expr::from_canonical(folded_lhs), i).to_expr(),
|
|
||||||
ops::ArrayIndex::new(Expr::from_canonical(folded_rhs), i).to_expr(),
|
|
||||||
flags,
|
|
||||||
)?;
|
|
||||||
retval = Some(match retval {
|
|
||||||
Some(old_eq) => old_eq & element_eq,
|
|
||||||
None => element_eq,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(retval.unwrap_or_else(|| {
|
|
||||||
StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
CanonicalType::Enum(unfolded_ty) => {
|
|
||||||
self.handle_enum_structural_eq(unfolded_ty, folded_lhs, folded_rhs, flags)
|
|
||||||
}
|
|
||||||
CanonicalType::Bundle(unfolded_ty) => {
|
|
||||||
let mut retval = None;
|
|
||||||
for (i, field) in unfolded_ty.fields().iter().enumerate() {
|
|
||||||
let field_eq = self.handle_structural_eq(
|
|
||||||
field.ty,
|
|
||||||
ops::FieldAccess::new_by_index(Expr::from_canonical(folded_lhs), i)
|
|
||||||
.to_expr(),
|
|
||||||
ops::FieldAccess::new_by_index(Expr::from_canonical(folded_rhs), i)
|
|
||||||
.to_expr(),
|
|
||||||
flags,
|
|
||||||
)?;
|
|
||||||
retval = Some(match retval {
|
|
||||||
Some(old_eq) => old_eq & field_eq,
|
|
||||||
None => field_eq,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(retval.unwrap_or_else(|| {
|
|
||||||
StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
CanonicalType::TraceAsString(unfolded_ty) => self.handle_structural_eq(
|
|
||||||
unfolded_ty.inner_ty(),
|
|
||||||
*Expr::<TraceAsString>::from_canonical(folded_lhs),
|
|
||||||
*Expr::<TraceAsString>::from_canonical(folded_rhs),
|
|
||||||
flags,
|
|
||||||
),
|
|
||||||
CanonicalType::UInt(_)
|
|
||||||
| CanonicalType::SInt(_)
|
|
||||||
| CanonicalType::Bool(_)
|
|
||||||
| CanonicalType::AsyncReset(_)
|
|
||||||
| CanonicalType::SyncReset(_)
|
|
||||||
| CanonicalType::Reset(_)
|
|
||||||
| CanonicalType::Clock(_)
|
|
||||||
| CanonicalType::PhantomConst(_)
|
|
||||||
| CanonicalType::DynSimOnly(_) => unreachable!("doesn't contain any enum types"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_port(
|
fn connect_port(
|
||||||
|
|
@ -774,9 +527,7 @@ fn connect_port(
|
||||||
rhs: Expr<CanonicalType>,
|
rhs: Expr<CanonicalType>,
|
||||||
source_location: SourceLocation,
|
source_location: SourceLocation,
|
||||||
) {
|
) {
|
||||||
let lhs = Expr::unwrap_transparent_types(lhs);
|
if Expr::ty(lhs) == Expr::ty(rhs) {
|
||||||
let rhs = Expr::unwrap_transparent_types(rhs);
|
|
||||||
if lhs.ty() == rhs.ty() {
|
|
||||||
stmts.push(
|
stmts.push(
|
||||||
StmtConnect {
|
StmtConnect {
|
||||||
lhs,
|
lhs,
|
||||||
|
|
@ -787,7 +538,7 @@ fn connect_port(
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match (lhs.ty(), rhs.ty()) {
|
match (Expr::ty(lhs), Expr::ty(rhs)) {
|
||||||
(CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => {
|
(CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => {
|
||||||
let lhs = Expr::<Bundle>::from_canonical(lhs);
|
let lhs = Expr::<Bundle>::from_canonical(lhs);
|
||||||
for field in lhs_type.fields() {
|
for field in lhs_type.fields() {
|
||||||
|
|
@ -821,9 +572,6 @@ fn connect_port(
|
||||||
connect_port(stmts, lhs[index], rhs[index], source_location);
|
connect_port(stmts, lhs[index], rhs[index], source_location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(CanonicalType::TraceAsString(_), CanonicalType::TraceAsString(_)) => {
|
|
||||||
unreachable!("handled by unwrap_transparent_types")
|
|
||||||
}
|
|
||||||
(CanonicalType::Bundle(_), _)
|
(CanonicalType::Bundle(_), _)
|
||||||
| (CanonicalType::Enum(_), _)
|
| (CanonicalType::Enum(_), _)
|
||||||
| (CanonicalType::Array(_), _)
|
| (CanonicalType::Array(_), _)
|
||||||
|
|
@ -835,11 +583,10 @@ fn connect_port(
|
||||||
| (CanonicalType::SyncReset(_), _)
|
| (CanonicalType::SyncReset(_), _)
|
||||||
| (CanonicalType::Reset(_), _)
|
| (CanonicalType::Reset(_), _)
|
||||||
| (CanonicalType::PhantomConst(_), _)
|
| (CanonicalType::PhantomConst(_), _)
|
||||||
| (CanonicalType::DynSimOnly(_), _)
|
| (CanonicalType::DynSimOnly(_), _) => unreachable!(
|
||||||
| (CanonicalType::TraceAsString(_), _) => unreachable!(
|
|
||||||
"trying to connect memory ports:\n{:?}\n{:?}",
|
"trying to connect memory ports:\n{:?}\n{:?}",
|
||||||
lhs.ty(),
|
Expr::ty(lhs),
|
||||||
rhs.ty(),
|
Expr::ty(rhs),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -859,23 +606,20 @@ fn match_int_tag(
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let mut retval = StmtIf {
|
let mut retval = StmtIf {
|
||||||
cond: int_tag_expr.cmp_eq(
|
cond: int_tag_expr
|
||||||
int_tag_expr
|
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)),
|
||||||
.ty()
|
|
||||||
.from_int_wrapping(next_to_last_variant_index),
|
|
||||||
),
|
|
||||||
source_location,
|
source_location,
|
||||||
blocks: [next_to_last_block, last_block],
|
blocks: [next_to_last_block, last_block],
|
||||||
};
|
};
|
||||||
for (variant_index, block) in blocks_iter.rev() {
|
for (variant_index, block) in blocks_iter.rev() {
|
||||||
retval = StmtIf {
|
retval = StmtIf {
|
||||||
cond: int_tag_expr.cmp_eq(int_tag_expr.ty().from_int_wrapping(variant_index)),
|
cond: int_tag_expr.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)),
|
||||||
source_location,
|
source_location,
|
||||||
blocks: [
|
blocks: [
|
||||||
block,
|
block,
|
||||||
Block {
|
Block {
|
||||||
memories: Default::default(),
|
memories: Default::default(),
|
||||||
stmts: [Stmt::from(retval)].intern_slice(),
|
stmts: [Stmt::from(retval)][..].intern(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
@ -893,8 +637,6 @@ impl Folder for State {
|
||||||
fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> {
|
fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> {
|
||||||
self.module_state_stack.push(ModuleState {
|
self.module_state_stack.push(ModuleState {
|
||||||
module_name: v.name_id(),
|
module_name: v.name_id(),
|
||||||
expr_cache: HashMap::default(),
|
|
||||||
source_location: v.source_location(),
|
|
||||||
});
|
});
|
||||||
let retval = Fold::default_fold(v, self);
|
let retval = Fold::default_fold(v, self);
|
||||||
self.module_state_stack.pop();
|
self.module_state_stack.pop();
|
||||||
|
|
@ -902,51 +644,30 @@ impl Folder for State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> {
|
fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> {
|
||||||
if let Some(folded_op) = self
|
match op {
|
||||||
.module_state_stack
|
|
||||||
.last()
|
|
||||||
.expect("known to be in module")
|
|
||||||
.expr_cache
|
|
||||||
.get(&op)
|
|
||||||
{
|
|
||||||
return Ok(*folded_op);
|
|
||||||
}
|
|
||||||
let folded_op = match op {
|
|
||||||
ExprEnum::EnumLiteral(op) => {
|
ExprEnum::EnumLiteral(op) => {
|
||||||
let folded_variant_value = op.variant_value().map(|v| v.fold(self)).transpose()?;
|
let folded_variant_value = op.variant_value().map(|v| v.fold(self)).transpose()?;
|
||||||
*Expr::expr_enum(self.handle_enum_literal(
|
Ok(*Expr::expr_enum(self.handle_enum_literal(
|
||||||
op.ty(),
|
op.ty(),
|
||||||
op.variant_index(),
|
op.variant_index(),
|
||||||
folded_variant_value,
|
folded_variant_value,
|
||||||
)?)
|
)?))
|
||||||
}
|
}
|
||||||
ExprEnum::VariantAccess(op) => {
|
ExprEnum::VariantAccess(op) => {
|
||||||
let folded_base_expr = Expr::canonical(op.base()).fold(self)?;
|
let folded_base_expr = Expr::canonical(op.base()).fold(self)?;
|
||||||
*Expr::expr_enum(self.handle_variant_access(
|
Ok(*Expr::expr_enum(self.handle_variant_access(
|
||||||
op.base().ty(),
|
Expr::ty(op.base()),
|
||||||
folded_base_expr,
|
folded_base_expr,
|
||||||
op.variant_index(),
|
op.variant_index(),
|
||||||
)?)
|
)?))
|
||||||
}
|
}
|
||||||
ExprEnum::StructuralEq(op) => {
|
ExprEnum::MemPort(mem_port) => Ok(
|
||||||
let ty = op.lhs().ty();
|
|
||||||
assert_eq!(ty, op.rhs().ty());
|
|
||||||
let folded_lhs = Expr::canonical(op.lhs()).fold(self)?;
|
|
||||||
let folded_rhs = Expr::canonical(op.rhs()).fold(self)?;
|
|
||||||
*Expr::expr_enum(self.handle_structural_eq(
|
|
||||||
ty,
|
|
||||||
folded_lhs,
|
|
||||||
folded_rhs,
|
|
||||||
op.flags(),
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
ExprEnum::MemPort(mem_port) => {
|
|
||||||
if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) {
|
if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) {
|
||||||
ExprEnum::Wire(wire)
|
ExprEnum::Wire(wire)
|
||||||
} else {
|
} else {
|
||||||
ExprEnum::MemPort(mem_port.fold(self)?)
|
ExprEnum::MemPort(mem_port.fold(self)?)
|
||||||
}
|
},
|
||||||
}
|
),
|
||||||
ExprEnum::UIntLiteral(_)
|
ExprEnum::UIntLiteral(_)
|
||||||
| ExprEnum::SIntLiteral(_)
|
| ExprEnum::SIntLiteral(_)
|
||||||
| ExprEnum::BoolLiteral(_)
|
| ExprEnum::BoolLiteral(_)
|
||||||
|
|
@ -1047,33 +768,21 @@ impl Folder for State {
|
||||||
| ExprEnum::SliceSInt(_)
|
| ExprEnum::SliceSInt(_)
|
||||||
| ExprEnum::CastToBits(_)
|
| ExprEnum::CastToBits(_)
|
||||||
| ExprEnum::CastBitsTo(_)
|
| ExprEnum::CastBitsTo(_)
|
||||||
| ExprEnum::TraceAsStringAsInner(_)
|
|
||||||
| ExprEnum::ToTraceAsString(_)
|
|
||||||
| ExprEnum::ModuleIO(_)
|
| ExprEnum::ModuleIO(_)
|
||||||
| ExprEnum::Instance(_)
|
| ExprEnum::Instance(_)
|
||||||
| ExprEnum::Wire(_)
|
| ExprEnum::Wire(_)
|
||||||
| ExprEnum::Reg(_)
|
| ExprEnum::Reg(_)
|
||||||
| ExprEnum::RegSync(_)
|
| ExprEnum::RegSync(_)
|
||||||
| ExprEnum::RegAsync(_)
|
| ExprEnum::RegAsync(_) => op.default_fold(self),
|
||||||
| ExprEnum::FormalInput(_)
|
}
|
||||||
| ExprEnum::SimIoForGlobal(_) => op.default_fold(self)?,
|
|
||||||
};
|
|
||||||
self.module_state_stack
|
|
||||||
.last_mut()
|
|
||||||
.expect("known to be in module")
|
|
||||||
.expr_cache
|
|
||||||
.insert(op, folded_op);
|
|
||||||
Ok(folded_op)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_block(&mut self, block: Block) -> Result<Block, Self::Error> {
|
fn fold_block(&mut self, block: Block) -> Result<Block, Self::Error> {
|
||||||
let block_scope = BlockScope::new(self, vec![], vec![]);
|
|
||||||
let this = &mut *block_scope.state;
|
|
||||||
let mut memories = vec![];
|
let mut memories = vec![];
|
||||||
let mut stmts = vec![];
|
let mut stmts = vec![];
|
||||||
for memory in block.memories {
|
for memory in block.memories {
|
||||||
let old_element_ty = memory.array_type().element();
|
let old_element_ty = memory.array_type().element();
|
||||||
let new_element_ty = memory.array_type().element().fold(this)?;
|
let new_element_ty = memory.array_type().element().fold(self)?;
|
||||||
if new_element_ty != old_element_ty {
|
if new_element_ty != old_element_ty {
|
||||||
let mut new_ports = vec![];
|
let mut new_ports = vec![];
|
||||||
for port in memory.ports() {
|
for port in memory.ports() {
|
||||||
|
|
@ -1099,7 +808,7 @@ impl Folder for State {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let wire = Wire::new_unchecked(
|
let wire = Wire::new_unchecked(
|
||||||
this.module_state_stack
|
self.module_state_stack
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.gen_name(&format!(
|
.gen_name(&format!(
|
||||||
|
|
@ -1123,7 +832,7 @@ impl Folder for State {
|
||||||
Expr::canonical(wire.to_expr()),
|
Expr::canonical(wire.to_expr()),
|
||||||
port.source_location(),
|
port.source_location(),
|
||||||
);
|
);
|
||||||
this.replacement_mem_ports.insert(port, wire.canonical());
|
self.replacement_mem_ports.insert(port, wire.canonical());
|
||||||
}
|
}
|
||||||
memories.push(Mem::new_unchecked(
|
memories.push(Mem::new_unchecked(
|
||||||
memory.scoped_name(),
|
memory.scoped_name(),
|
||||||
|
|
@ -1138,12 +847,10 @@ impl Folder for State {
|
||||||
memory.mem_annotations(),
|
memory.mem_annotations(),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
memories.push(memory.fold(this)?);
|
memories.push(memory.fold(self)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stmts.extend_from_slice(&block.stmts.fold(this)?);
|
stmts.extend_from_slice(&block.stmts.fold(self)?);
|
||||||
stmts.splice(0..0, this.new_prefix_stmts_for_block.drain(..));
|
|
||||||
stmts.extend_from_slice(&this.new_suffix_stmts_for_block);
|
|
||||||
Ok(Block {
|
Ok(Block {
|
||||||
memories: Intern::intern_owned(memories),
|
memories: Intern::intern_owned(memories),
|
||||||
stmts: Intern::intern_owned(stmts),
|
stmts: Intern::intern_owned(stmts),
|
||||||
|
|
@ -1159,7 +866,7 @@ impl Folder for State {
|
||||||
}) => {
|
}) => {
|
||||||
let folded_expr = Expr::canonical(expr).fold(self)?;
|
let folded_expr = Expr::canonical(expr).fold(self)?;
|
||||||
let folded_blocks = blocks.fold(self)?;
|
let folded_blocks = blocks.fold(self)?;
|
||||||
self.handle_match(expr.ty(), folded_expr, source_location, &folded_blocks)
|
self.handle_match(Expr::ty(expr), folded_expr, source_location, &folded_blocks)
|
||||||
}
|
}
|
||||||
Stmt::Connect(StmtConnect {
|
Stmt::Connect(StmtConnect {
|
||||||
lhs,
|
lhs,
|
||||||
|
|
@ -1170,8 +877,8 @@ impl Folder for State {
|
||||||
let folded_rhs = rhs.fold(self)?;
|
let folded_rhs = rhs.fold(self)?;
|
||||||
let mut output_stmts = vec![];
|
let mut output_stmts = vec![];
|
||||||
self.handle_stmt_connect(
|
self.handle_stmt_connect(
|
||||||
lhs.ty(),
|
Expr::ty(lhs),
|
||||||
rhs.ty(),
|
Expr::ty(rhs),
|
||||||
folded_lhs,
|
folded_lhs,
|
||||||
folded_rhs,
|
folded_rhs,
|
||||||
source_location,
|
source_location,
|
||||||
|
|
@ -1225,8 +932,7 @@ impl Folder for State {
|
||||||
| CanonicalType::SyncReset(_)
|
| CanonicalType::SyncReset(_)
|
||||||
| CanonicalType::Reset(_)
|
| CanonicalType::Reset(_)
|
||||||
| CanonicalType::PhantomConst(_)
|
| CanonicalType::PhantomConst(_)
|
||||||
| CanonicalType::DynSimOnly(_)
|
| CanonicalType::DynSimOnly(_) => canonical_type.default_fold(self),
|
||||||
| CanonicalType::TraceAsString(_) => canonical_type.default_fold(self),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1249,15 +955,12 @@ impl Folder for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum, Serialize, Deserialize)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum SimplifyEnumsKind {
|
pub enum SimplifyEnumsKind {
|
||||||
SimplifyToEnumsWithNoBody,
|
SimplifyToEnumsWithNoBody,
|
||||||
#[clap(name = "replace-with-bundle-of-uints")]
|
#[clap(name = "replace-with-bundle-of-uints")]
|
||||||
#[serde(rename = "replace-with-bundle-of-uints")]
|
|
||||||
ReplaceWithBundleOfUInts,
|
ReplaceWithBundleOfUInts,
|
||||||
#[clap(name = "replace-with-uint")]
|
#[clap(name = "replace-with-uint")]
|
||||||
#[serde(rename = "replace-with-uint")]
|
|
||||||
ReplaceWithUInt,
|
ReplaceWithUInt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1265,13 +968,10 @@ pub fn simplify_enums(
|
||||||
module: Interned<Module<Bundle>>,
|
module: Interned<Module<Bundle>>,
|
||||||
kind: SimplifyEnumsKind,
|
kind: SimplifyEnumsKind,
|
||||||
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
|
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
|
||||||
let module = deduce_structural_eq_flags(module);
|
|
||||||
module.fold(&mut State {
|
module.fold(&mut State {
|
||||||
enum_types: HashMap::default(),
|
enum_types: HashMap::default(),
|
||||||
replacement_mem_ports: HashMap::default(),
|
replacement_mem_ports: HashMap::default(),
|
||||||
kind,
|
kind,
|
||||||
module_state_stack: vec![],
|
module_state_stack: vec![],
|
||||||
new_prefix_stmts_for_block: vec![],
|
|
||||||
new_suffix_stmts_for_block: vec![],
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
annotations::TargetedAnnotation,
|
annotations::TargetedAnnotation,
|
||||||
array::Array,
|
array::Array,
|
||||||
bundle::{Bundle, BundleType},
|
bundle::{Bundle, BundleType},
|
||||||
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr, ValueType},
|
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr},
|
||||||
int::{Bool, SInt, Size, UInt},
|
int::{Bool, SInt, Size, UInt},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
memory::{Mem, MemPort, PortType},
|
memory::{Mem, MemPort, PortType},
|
||||||
|
|
@ -90,7 +90,7 @@ impl MemSplit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn new(element_type: CanonicalType) -> Self {
|
fn new(element_type: CanonicalType) -> Self {
|
||||||
match element_type.unwrap_transparent_types() {
|
match element_type {
|
||||||
CanonicalType::Bundle(bundle_ty) => MemSplit::Bundle {
|
CanonicalType::Bundle(bundle_ty) => MemSplit::Bundle {
|
||||||
fields: bundle_ty
|
fields: bundle_ty
|
||||||
.fields()
|
.fields()
|
||||||
|
|
@ -195,7 +195,6 @@ impl MemSplit {
|
||||||
| CanonicalType::SyncReset(_)
|
| CanonicalType::SyncReset(_)
|
||||||
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
|
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||||
CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"),
|
CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"),
|
||||||
CanonicalType::TraceAsString(_) => unreachable!("handled by unwrap_transparent_types"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -307,9 +306,7 @@ impl SplitMemState<'_, '_> {
|
||||||
let outer_mem_name_path_len = self.mem_name_path.len();
|
let outer_mem_name_path_len = self.mem_name_path.len();
|
||||||
match self.split {
|
match self.split {
|
||||||
MemSplit::Bundle { fields } => {
|
MemSplit::Bundle { fields } => {
|
||||||
let CanonicalType::Bundle(bundle_type) =
|
let CanonicalType::Bundle(bundle_type) = self.element_type else {
|
||||||
self.element_type.unwrap_transparent_types()
|
|
||||||
else {
|
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
for ((field, field_offset), split) in bundle_type
|
for ((field, field_offset), split) in bundle_type
|
||||||
|
|
@ -324,10 +321,7 @@ impl SplitMemState<'_, '_> {
|
||||||
let field_ty_bit_width = field.ty.bit_width();
|
let field_ty_bit_width = field.ty.bit_width();
|
||||||
self.split_state_stack.push_map(
|
self.split_state_stack.push_map(
|
||||||
|e: Expr<CanonicalType>| {
|
|e: Expr<CanonicalType>| {
|
||||||
Expr::field(
|
Expr::field(Expr::<Bundle>::from_canonical(e), &field.name)
|
||||||
Expr::<Bundle>::from_canonical(Expr::unwrap_transparent_types(e)),
|
|
||||||
&field.name,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
|initial_value_element| {
|
|initial_value_element| {
|
||||||
let Some(field_offset) = field_offset.only_bit_width() else {
|
let Some(field_offset) = field_offset.only_bit_width() else {
|
||||||
|
|
@ -383,8 +377,8 @@ impl SplitMemState<'_, '_> {
|
||||||
};
|
};
|
||||||
self.output_stmts.push(
|
self.output_stmts.push(
|
||||||
StmtConnect {
|
StmtConnect {
|
||||||
lhs: Expr::unwrap_transparent_types(Expr::field(port_expr, name)),
|
lhs: Expr::field(port_expr, name),
|
||||||
rhs: Expr::unwrap_transparent_types(Expr::field(wire_expr, name)),
|
rhs: Expr::field(wire_expr, name),
|
||||||
source_location: port.source_location(),
|
source_location: port.source_location(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -395,8 +389,7 @@ impl SplitMemState<'_, '_> {
|
||||||
self.output_mems.push(new_mem);
|
self.output_mems.push(new_mem);
|
||||||
}
|
}
|
||||||
MemSplit::Array { elements } => {
|
MemSplit::Array { elements } => {
|
||||||
let CanonicalType::Array(array_type) = self.element_type.unwrap_transparent_types()
|
let CanonicalType::Array(array_type) = self.element_type else {
|
||||||
else {
|
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
let element_type = array_type.element();
|
let element_type = array_type.element();
|
||||||
|
|
@ -405,7 +398,7 @@ impl SplitMemState<'_, '_> {
|
||||||
self.mem_name_path.truncate(outer_mem_name_path_len);
|
self.mem_name_path.truncate(outer_mem_name_path_len);
|
||||||
write!(self.mem_name_path, "_{index}").unwrap();
|
write!(self.mem_name_path, "_{index}").unwrap();
|
||||||
self.split_state_stack.push_map(
|
self.split_state_stack.push_map(
|
||||||
|e| Expr::<Array>::from_canonical(Expr::unwrap_transparent_types(e))[index],
|
|e| Expr::<Array>::from_canonical(e)[index],
|
||||||
|initial_value_element| {
|
|initial_value_element| {
|
||||||
&initial_value_element[index * element_bit_width..][..element_bit_width]
|
&initial_value_element[index * element_bit_width..][..element_bit_width]
|
||||||
},
|
},
|
||||||
|
|
@ -471,7 +464,7 @@ impl ModuleState {
|
||||||
assert_eq!(memory_element_array_range_len % input_array_type.len(), 0);
|
assert_eq!(memory_element_array_range_len % input_array_type.len(), 0);
|
||||||
let chunk_size = memory_element_array_range_len / input_array_type.len();
|
let chunk_size = memory_element_array_range_len / input_array_type.len();
|
||||||
for index in 0..input_array_type.len() {
|
for index in 0..input_array_type.len() {
|
||||||
let map = |e| Expr::<Array>::from_canonical(Expr::unwrap_transparent_types(e))[index];
|
let map = |e| Expr::<Array>::from_canonical(e)[index];
|
||||||
let wire_rdata = wire_rdata.map(map);
|
let wire_rdata = wire_rdata.map(map);
|
||||||
let wire_wdata = wire_wdata.map(map);
|
let wire_wdata = wire_wdata.map(map);
|
||||||
let wire_wmask = wire_wmask.map(map);
|
let wire_wmask = wire_wmask.map(map);
|
||||||
|
|
@ -512,8 +505,8 @@ impl ModuleState {
|
||||||
port_read: Expr<CanonicalType>| {
|
port_read: Expr<CanonicalType>| {
|
||||||
output_stmts.push(
|
output_stmts.push(
|
||||||
StmtConnect {
|
StmtConnect {
|
||||||
lhs: Expr::unwrap_transparent_types(wire_read),
|
lhs: wire_read,
|
||||||
rhs: Expr::unwrap_transparent_types(port_read),
|
rhs: port_read,
|
||||||
source_location,
|
source_location,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -524,8 +517,8 @@ impl ModuleState {
|
||||||
port_write: Expr<CanonicalType>| {
|
port_write: Expr<CanonicalType>| {
|
||||||
output_stmts.push(
|
output_stmts.push(
|
||||||
StmtConnect {
|
StmtConnect {
|
||||||
lhs: Expr::unwrap_transparent_types(port_write),
|
lhs: port_write,
|
||||||
rhs: Expr::unwrap_transparent_types(wire_write),
|
rhs: wire_write,
|
||||||
source_location,
|
source_location,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -537,8 +530,7 @@ impl ModuleState {
|
||||||
connect_read(
|
connect_read(
|
||||||
output_stmts,
|
output_stmts,
|
||||||
wire_read,
|
wire_read,
|
||||||
Expr::<UInt>::from_canonical(Expr::unwrap_transparent_types(port_read))
|
Expr::<UInt>::from_canonical(port_read).cast_bits_to(Expr::ty(wire_read)),
|
||||||
.cast_bits_to(wire_read.ty()),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
let connect_write_enum =
|
let connect_write_enum =
|
||||||
|
|
@ -552,7 +544,7 @@ impl ModuleState {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
loop {
|
loop {
|
||||||
match input_element_type.unwrap_transparent_types() {
|
match input_element_type {
|
||||||
CanonicalType::Bundle(_) => {
|
CanonicalType::Bundle(_) => {
|
||||||
unreachable!("bundle types are always split")
|
unreachable!("bundle types are always split")
|
||||||
}
|
}
|
||||||
|
|
@ -633,9 +625,6 @@ impl ModuleState {
|
||||||
| CanonicalType::SyncReset(_)
|
| CanonicalType::SyncReset(_)
|
||||||
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
|
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||||
CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"),
|
CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"),
|
||||||
CanonicalType::TraceAsString(_) => {
|
|
||||||
unreachable!("handled by unwrap_transparent_types")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,19 @@ use crate::{
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
enum_::{Enum, EnumType, EnumVariant},
|
enum_::{Enum, EnumType, EnumVariant},
|
||||||
expr::{
|
expr::{
|
||||||
Expr, ExprEnum, ValueType, ops,
|
Expr, ExprEnum, ops,
|
||||||
target::{
|
target::{
|
||||||
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
|
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
|
||||||
TargetPathDynArrayElement, TargetPathElement, TargetPathToTraceAsString,
|
TargetPathDynArrayElement, TargetPathElement,
|
||||||
TargetPathTraceAsStringInner,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formal::{FormalInput, FormalInputKind, FormalKind},
|
formal::FormalKind,
|
||||||
int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue},
|
int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
|
memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
|
||||||
module::{
|
module::{
|
||||||
AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter,
|
AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter,
|
||||||
ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId, NameIdOrGlobal,
|
ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId,
|
||||||
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf,
|
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf,
|
||||||
StmtInstance, StmtMatch, StmtReg, StmtWire,
|
StmtInstance, StmtMatch, StmtReg, StmtWire,
|
||||||
},
|
},
|
||||||
|
|
@ -33,10 +32,7 @@ use crate::{
|
||||||
reset::{AsyncReset, Reset, ResetType, SyncReset},
|
reset::{AsyncReset, Reset, ResetType, SyncReset},
|
||||||
sim::{ExternModuleSimulation, value::DynSimOnly},
|
sim::{ExternModuleSimulation, value::DynSimOnly},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{CanonicalType, TraceAsString, Type},
|
ty::{CanonicalType, Type},
|
||||||
vendor::xilinx::{
|
|
||||||
XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation,
|
|
||||||
},
|
|
||||||
wire::Wire,
|
wire::Wire,
|
||||||
};
|
};
|
||||||
use num_bigint::{BigInt, BigUint};
|
use num_bigint::{BigInt, BigUint};
|
||||||
|
|
@ -482,30 +478,4 @@ impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ mut
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State: ?Sized + Visitor> Visit<State> for NameIdOrGlobal {
|
|
||||||
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
|
|
||||||
state.visit_name_id_or_global(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
|
|
||||||
match self {
|
|
||||||
Self::Global => Ok(()),
|
|
||||||
Self::NameId(name_id) => name_id.visit(state),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<State: ?Sized + Folder> Fold<State> for NameIdOrGlobal {
|
|
||||||
fn fold(self, state: &mut State) -> Result<Self, <State>::Error> {
|
|
||||||
state.fold_name_id_or_global(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_fold(self, state: &mut State) -> Result<Self, <State>::Error> {
|
|
||||||
match self {
|
|
||||||
Self::Global => Ok(Self::Global),
|
|
||||||
Self::NameId(name_id) => Ok(Self::NameId(name_id.fold(state)?)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/visit.rs"));
|
include!(concat!(env!("OUT_DIR"), "/visit.rs"));
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,17 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{Expr, HdlPartialEqImpl, HdlPartialOrdImpl, ToExpr, ValueType},
|
expr::{
|
||||||
|
Expr, ToExpr,
|
||||||
|
ops::{ExprPartialEq, ExprPartialOrd},
|
||||||
|
},
|
||||||
int::Bool,
|
int::Bool,
|
||||||
intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize},
|
intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize},
|
||||||
sim::value::{SimValue, ToSimValue, ToSimValueWithType},
|
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{
|
ty::{
|
||||||
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
|
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
|
||||||
SimValueDebug, StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
||||||
serde_impls::{SerdeCanonicalType, SerdePhantomConst},
|
serde_impls::{SerdeCanonicalType, SerdePhantomConst},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -19,7 +22,6 @@ use serde::{
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
borrow::Cow,
|
|
||||||
fmt,
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
|
|
@ -129,7 +131,7 @@ impl<T: Type + PhantomConstValue> Index<T> for PhantomConstWithoutGenerics {
|
||||||
type Output = PhantomConst<T>;
|
type Output = PhantomConst<T>;
|
||||||
|
|
||||||
fn index(&self, value: T) -> &Self::Output {
|
fn index(&self, value: T) -> &Self::Output {
|
||||||
Interned::into_inner(PhantomConst::new(&value).intern_sized())
|
Interned::into_inner(PhantomConst::new(value.intern()).intern_sized())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,37 +222,16 @@ impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
||||||
pub fn new_interned(value: Interned<T>) -> Self {
|
pub fn new(value: Interned<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
value: LazyInterned::Interned(value),
|
value: LazyInterned::Interned(value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_sized(value: T) -> Self
|
pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
Self::new_interned(value.intern_sized())
|
|
||||||
}
|
|
||||||
pub fn new(value: &T) -> Self {
|
|
||||||
Self::new_interned(value.intern())
|
|
||||||
}
|
|
||||||
pub fn new_deref<U: Intern + std::ops::Deref<Target = T>>(value: U) -> Self
|
|
||||||
where
|
|
||||||
T: ToOwned<Owned = U>,
|
|
||||||
{
|
|
||||||
Self::new_interned(value.intern_deref())
|
|
||||||
}
|
|
||||||
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
value: LazyInterned::new_const::<V>(),
|
value: LazyInterned::new_lazy(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub const fn new_const_default() -> Self
|
|
||||||
where
|
|
||||||
Interned<T>: Default,
|
|
||||||
{
|
|
||||||
Self::new_const::<Interned<T>>()
|
|
||||||
}
|
|
||||||
pub fn get(self) -> Interned<T> {
|
pub fn get(self) -> Interned<T> {
|
||||||
self.value.interned()
|
self.value.interned()
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +245,7 @@ impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
||||||
if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) {
|
if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) {
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
<PhantomConst>::new_interned(
|
<PhantomConst>::new(
|
||||||
PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()),
|
PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +253,7 @@ impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
||||||
if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) {
|
if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) {
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
Self::new_interned(
|
Self::new(
|
||||||
PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()),
|
PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -327,15 +308,6 @@ impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + PhantomConstValue> SimValueDebug for PhantomConst<T> {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + PhantomConstValue> Default for PhantomConst<T>
|
impl<T: ?Sized + PhantomConstValue> Default for PhantomConst<T>
|
||||||
where
|
where
|
||||||
Interned<T>: Default,
|
Interned<T>: Default,
|
||||||
|
|
@ -349,7 +321,9 @@ impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T>
|
||||||
where
|
where
|
||||||
Interned<T>: Default,
|
Interned<T>: Default,
|
||||||
{
|
{
|
||||||
const TYPE: Self = Self::new_const_default();
|
const TYPE: Self = PhantomConst {
|
||||||
|
value: LazyInterned::new_lazy(&Interned::<T>::default),
|
||||||
|
};
|
||||||
const MASK_TYPE: Self::MaskType = ();
|
const MASK_TYPE: Self::MaskType = ();
|
||||||
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
||||||
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
||||||
|
|
@ -372,9 +346,7 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
match SerdeType::<T>::deserialize(deserializer)? {
|
match SerdeType::<T>::deserialize(deserializer)? {
|
||||||
SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => {
|
SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => Ok(Self::new(value)),
|
||||||
Ok(Self::new_interned(value))
|
|
||||||
}
|
|
||||||
ty => Err(Error::invalid_value(
|
ty => Err(Error::invalid_value(
|
||||||
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
|
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
|
||||||
&"a PhantomConst",
|
&"a PhantomConst",
|
||||||
|
|
@ -383,104 +355,50 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + PhantomConstValue> HdlPartialEqImpl<Self> for PhantomConst<T> {
|
impl<T: ?Sized + PhantomConstValue> ExprPartialEq<Self> for PhantomConst<T> {
|
||||||
const TRY_STRUCTURAL_EQ: bool = true;
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
|
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_eq(
|
|
||||||
lhs: Self,
|
|
||||||
_lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
rhs: Self,
|
|
||||||
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
assert_eq!(lhs, rhs);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
|
||||||
assert_eq!(lhs.ty(), rhs.ty());
|
|
||||||
true.to_expr()
|
true.to_expr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||||
assert_eq!(lhs.ty(), rhs.ty());
|
|
||||||
false.to_expr()
|
false.to_expr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + PhantomConstValue> HdlPartialOrdImpl<Self> for PhantomConst<T> {
|
impl<T: ?Sized + PhantomConstValue> ExprPartialOrd<Self> for PhantomConst<T> {
|
||||||
#[track_caller]
|
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
fn cmp_value_lt(
|
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||||
lhs: Self,
|
|
||||||
_lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
rhs: Self,
|
|
||||||
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
assert_eq!(lhs, rhs);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_le(
|
|
||||||
lhs: Self,
|
|
||||||
_lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
rhs: Self,
|
|
||||||
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
assert_eq!(lhs, rhs);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_gt(
|
|
||||||
lhs: Self,
|
|
||||||
_lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
rhs: Self,
|
|
||||||
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
assert_eq!(lhs, rhs);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_ge(
|
|
||||||
lhs: Self,
|
|
||||||
_lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
rhs: Self,
|
|
||||||
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
assert_eq!(lhs, rhs);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
|
||||||
assert_eq!(lhs.ty(), rhs.ty());
|
|
||||||
false.to_expr()
|
false.to_expr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
fn cmp_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||||
assert_eq!(lhs.ty(), rhs.ty());
|
|
||||||
true.to_expr()
|
true.to_expr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||||
assert_eq!(lhs.ty(), rhs.ty());
|
|
||||||
false.to_expr()
|
false.to_expr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||||
assert_eq!(lhs.ty(), rhs.ty());
|
|
||||||
true.to_expr()
|
true.to_expr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> SimValuePartialEq<Self> for PhantomConst<T> {
|
||||||
|
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
|
||||||
|
assert_eq!(SimValue::ty(this), SimValue::ty(other));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + PhantomConstValue> ToSimValue for PhantomConst<T> {
|
impl<T: ?Sized + PhantomConstValue> ToSimValue for PhantomConst<T> {
|
||||||
|
type Type = PhantomConst<T>;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_value(*self, *self)
|
SimValue::from_value(*self, *self)
|
||||||
}
|
}
|
||||||
|
|
@ -497,71 +415,3 @@ impl<T: ?Sized + PhantomConstValue> ToSimValueWithType<CanonicalType> for Phanto
|
||||||
SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self))
|
SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod sealed {
|
|
||||||
pub trait Sealed<T: ?Sized> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PhantomConstGet<T: ?Sized + PhantomConstValue>: sealed::Sealed<T> {
|
|
||||||
fn get(&self) -> Interned<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + PhantomConstValue, This: ?Sized + std::ops::Deref<Target: PhantomConstGet<T>>>
|
|
||||||
sealed::Sealed<T> for This
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + PhantomConstValue, This: ?Sized + std::ops::Deref<Target: PhantomConstGet<T>>>
|
|
||||||
PhantomConstGet<T> for This
|
|
||||||
{
|
|
||||||
fn get(&self) -> Interned<T> {
|
|
||||||
This::Target::get(&**self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_phantom_const_get {
|
|
||||||
(
|
|
||||||
impl PhantomConstGet<$T:ident> for $ty:ty {
|
|
||||||
fn $get:ident(&$get_self:ident) -> _ $get_body:block
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
impl<$T: ?Sized + PhantomConstValue> sealed::Sealed<$T> for $ty {}
|
|
||||||
|
|
||||||
impl<$T: ?Sized + PhantomConstValue> PhantomConstGet<$T> for $ty {
|
|
||||||
fn $get(&$get_self) -> Interned<$T> $get_body
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_phantom_const_get! {
|
|
||||||
impl PhantomConstGet<T> for PhantomConst<T> {
|
|
||||||
fn get(&self) -> _ {
|
|
||||||
PhantomConst::get(*self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_phantom_const_get! {
|
|
||||||
impl PhantomConstGet<T> for Expr<PhantomConst<T>> {
|
|
||||||
fn get(&self) -> _ {
|
|
||||||
PhantomConst::get(self.ty())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub trait ReturnSelfUnchanged<T: ?Sized> {
|
|
||||||
type Type: ?Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<This: ?Sized, T: ?Sized> ReturnSelfUnchanged<T> for This {
|
|
||||||
type Type = This;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn type_alias_phantom_const_get_helper<T: ?Sized + PhantomConstValue, R: Intern + Clone>(
|
|
||||||
param: impl PhantomConstGet<T>,
|
|
||||||
get: impl FnOnce(Interned<T>) -> R,
|
|
||||||
) -> &'static R {
|
|
||||||
Interned::into_inner(get(param.get()).intern_sized())
|
|
||||||
}
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,59 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use ordered_float::NotNan;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct ClockInputProperties {
|
|
||||||
pub frequency: NotNan<f64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(no_runtime_generics, no_static)]
|
|
||||||
pub struct ClockInput {
|
|
||||||
pub clk: Clock,
|
|
||||||
pub properties: PhantomConst<ClockInputProperties>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClockInput {
|
|
||||||
#[track_caller]
|
|
||||||
pub fn new(frequency: f64) -> Self {
|
|
||||||
assert!(
|
|
||||||
frequency > 0.0 && frequency.is_finite(),
|
|
||||||
"invalid clock frequency: {frequency}"
|
|
||||||
);
|
|
||||||
Self {
|
|
||||||
clk: Clock,
|
|
||||||
properties: PhantomConst::new_sized(ClockInputProperties {
|
|
||||||
frequency: NotNan::new(frequency).expect("just checked"),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn frequency(self) -> f64 {
|
|
||||||
self.properties.get().frequency.into_inner()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
pub struct Led {
|
|
||||||
pub on: Bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
pub struct RgbLed {
|
|
||||||
pub r: Bool,
|
|
||||||
pub g: Bool,
|
|
||||||
pub b: Bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
/// UART, used as an output from the FPGA
|
|
||||||
pub struct Uart {
|
|
||||||
/// transmit from the FPGA's perspective
|
|
||||||
pub tx: Bool,
|
|
||||||
/// receive from the FPGA's perspective
|
|
||||||
#[hdl(flip)]
|
|
||||||
pub rx: Bool,
|
|
||||||
}
|
|
||||||
|
|
@ -7,17 +7,17 @@ pub use crate::{
|
||||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
|
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
|
||||||
},
|
},
|
||||||
array::{Array, ArrayType},
|
array::{Array, ArrayType},
|
||||||
build::{BuildCli, JobParams, RunBuild},
|
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
|
cli::Cli,
|
||||||
clock::{Clock, ClockDomain, ToClock},
|
clock::{Clock, ClockDomain, ToClock},
|
||||||
enum_::{Enum, HdlNone, HdlOption, HdlSome},
|
enum_::{Enum, HdlNone, HdlOption, HdlSome},
|
||||||
expr::{
|
expr::{
|
||||||
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr,
|
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr,
|
||||||
ReduceBits, ToExpr, ToTraceAsString, ValueType, repeat,
|
ReduceBits, ToExpr, repeat,
|
||||||
},
|
},
|
||||||
formal::{
|
formal::{
|
||||||
all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, hdl_assert,
|
MakeFormalExpr, all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset,
|
||||||
hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover,
|
hdl_assert, hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover,
|
||||||
hdl_cover_with_enable,
|
hdl_cover_with_enable,
|
||||||
},
|
},
|
||||||
hdl, hdl_module,
|
hdl, hdl_module,
|
||||||
|
|
@ -27,8 +27,7 @@ pub use crate::{
|
||||||
Instance, Module, ModuleBuilder, annotate, connect, connect_any, incomplete_wire, instance,
|
Instance, Module, ModuleBuilder, annotate, connect, connect_any, incomplete_wire, instance,
|
||||||
memory, memory_array, memory_with_init, reg_builder, wire,
|
memory, memory_array, memory_with_init, reg_builder, wire,
|
||||||
},
|
},
|
||||||
phantom_const::{PhantomConst, PhantomConstGet},
|
phantom_const::PhantomConst,
|
||||||
platform::{DynPlatform, Platform, PlatformIOBuilder, peripherals},
|
|
||||||
reg::Reg,
|
reg::Reg,
|
||||||
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
||||||
sim::{
|
sim::{
|
||||||
|
|
@ -37,8 +36,7 @@ pub use crate::{
|
||||||
value::{SimOnly, SimOnlyValue, SimValue, ToSimValue, ToSimValueWithType},
|
value::{SimOnly, SimOnlyValue, SimValue, ToSimValue, ToSimValueWithType},
|
||||||
},
|
},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
testing::{FormalMode, assert_formal, checked_vcd_output},
|
ty::{AsMask, CanonicalType, Type},
|
||||||
ty::{AsMask, CanonicalType, TraceAsString, Type},
|
|
||||||
util::{ConstUsize, GenericConstUsize},
|
util::{ConstUsize, GenericConstUsize},
|
||||||
wire::Wire,
|
wire::Wire,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::ClockDomain,
|
clock::ClockDomain,
|
||||||
expr::{Expr, Flow, ValueType, value_category::ValueCategoryExpr},
|
expr::{Expr, Flow},
|
||||||
intern::Interned,
|
intern::Interned,
|
||||||
module::{NameId, ScopedNameId},
|
module::{NameId, ScopedNameId},
|
||||||
reset::{Reset, ResetType},
|
reset::{Reset, ResetType},
|
||||||
|
|
@ -20,16 +20,7 @@ pub struct Reg<T: Type, R: ResetType = Reset> {
|
||||||
init: Option<Expr<T>>,
|
init: Option<Expr<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type, R: ResetType> ValueType for Reg<T, R> {
|
impl<T: Type + fmt::Debug, R: ResetType> fmt::Debug for Reg<T, R> {
|
||||||
type Type = T;
|
|
||||||
type ValueCategory = ValueCategoryExpr;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
self.ty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, R: ResetType> fmt::Debug for Reg<T, R> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let Self {
|
let Self {
|
||||||
name,
|
name,
|
||||||
|
|
@ -77,9 +68,8 @@ impl<T: Type, R: ResetType> Reg<T, R> {
|
||||||
"register type must be a storable type"
|
"register type must be a storable type"
|
||||||
);
|
);
|
||||||
if let Some(init) = init {
|
if let Some(init) = init {
|
||||||
assert_eq!(ty, init.ty(), "register's type must match init type");
|
assert_eq!(ty, Expr::ty(init), "register's type must match init type");
|
||||||
}
|
}
|
||||||
scoped_name.0.assert_is_name_id();
|
|
||||||
Self {
|
Self {
|
||||||
name: scoped_name,
|
name: scoped_name,
|
||||||
source_location,
|
source_location,
|
||||||
|
|
@ -88,6 +78,9 @@ impl<T: Type, R: ResetType> Reg<T, R> {
|
||||||
init,
|
init,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn ty(&self) -> T {
|
||||||
|
self.ty
|
||||||
|
}
|
||||||
pub fn source_location(&self) -> SourceLocation {
|
pub fn source_location(&self) -> SourceLocation {
|
||||||
self.source_location
|
self.source_location
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +88,7 @@ impl<T: Type, R: ResetType> Reg<T, R> {
|
||||||
self.containing_module_name_id().0
|
self.containing_module_name_id().0
|
||||||
}
|
}
|
||||||
pub fn containing_module_name_id(&self) -> NameId {
|
pub fn containing_module_name_id(&self) -> NameId {
|
||||||
self.name.0.unwrap_name_id()
|
self.name.0
|
||||||
}
|
}
|
||||||
pub fn name(&self) -> Interned<str> {
|
pub fn name(&self) -> Interned<str> {
|
||||||
self.name_id().0
|
self.name_id().0
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,16 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
expr::{CastToImpl, Expr, ValueType},
|
expr::{Expr, ToExpr, ops},
|
||||||
int::{Bool, SInt, SIntValue, UInt, UIntValue},
|
int::{Bool, SInt, UInt},
|
||||||
sim::value::SimValue,
|
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{
|
ty::{
|
||||||
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
||||||
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties,
|
OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
||||||
impl_match_variant_as_self,
|
|
||||||
},
|
},
|
||||||
util::ConstUsize,
|
|
||||||
};
|
};
|
||||||
use bitvec::{bits, order::Lsb0};
|
use bitvec::{bits, order::Lsb0};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
pub trait ResetTypeSealed {}
|
pub trait ResetTypeSealed {}
|
||||||
|
|
@ -24,15 +19,15 @@ mod sealed {
|
||||||
pub trait ResetType:
|
pub trait ResetType:
|
||||||
StaticType<MaskType = Bool>
|
StaticType<MaskType = Bool>
|
||||||
+ sealed::ResetTypeSealed
|
+ sealed::ResetTypeSealed
|
||||||
+ CastToImpl<Bool, ValueOutput = bool>
|
+ ops::ExprCastTo<Bool>
|
||||||
+ CastToImpl<Reset, ValueOutput = SimValue<Reset>>
|
+ ops::ExprCastTo<Reset>
|
||||||
+ CastToImpl<SyncReset, ValueOutput = SimValue<SyncReset>>
|
+ ops::ExprCastTo<SyncReset>
|
||||||
+ CastToImpl<AsyncReset, ValueOutput = SimValue<AsyncReset>>
|
+ ops::ExprCastTo<AsyncReset>
|
||||||
+ CastToImpl<Clock, ValueOutput = SimValue<Clock>>
|
+ ops::ExprCastTo<Clock>
|
||||||
+ CastToImpl<UInt<1>, ValueOutput = UIntValue<ConstUsize<1>>>
|
+ ops::ExprCastTo<UInt<1>>
|
||||||
+ CastToImpl<SInt<1>, ValueOutput = SIntValue<ConstUsize<1>>>
|
+ ops::ExprCastTo<SInt<1>>
|
||||||
+ CastToImpl<UInt, ValueOutput = UIntValue>
|
+ ops::ExprCastTo<UInt>
|
||||||
+ CastToImpl<SInt, ValueOutput = SIntValue>
|
+ ops::ExprCastTo<SInt>
|
||||||
{
|
{
|
||||||
fn dispatch<D: ResetTypeDispatch>(input: D::Input<Self>, dispatch: D) -> D::Output<Self>;
|
fn dispatch<D: ResetTypeDispatch>(input: D::Input<Self>, dispatch: D) -> D::Output<Self>;
|
||||||
}
|
}
|
||||||
|
|
@ -103,15 +98,6 @@ macro_rules! reset_type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimValueDebug for $name {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
pub fn type_properties(self) -> TypeProperties {
|
pub fn type_properties(self) -> TypeProperties {
|
||||||
Self::TYPE_PROPERTIES
|
Self::TYPE_PROPERTIES
|
||||||
|
|
@ -146,34 +132,29 @@ macro_rules! reset_type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait $Trait {
|
pub trait $Trait {
|
||||||
type Output: ValueType<Type = $name>;
|
fn $trait_fn(&self) -> Expr<$name>;
|
||||||
fn $trait_fn(&self) -> Self::Output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + $Trait> $Trait for &'_ T {
|
impl<T: ?Sized + $Trait> $Trait for &'_ T {
|
||||||
type Output = T::Output;
|
fn $trait_fn(&self) -> Expr<$name> {
|
||||||
fn $trait_fn(&self) -> Self::Output {
|
|
||||||
(**self).$trait_fn()
|
(**self).$trait_fn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
|
impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
|
||||||
type Output = T::Output;
|
fn $trait_fn(&self) -> Expr<$name> {
|
||||||
fn $trait_fn(&self) -> Self::Output {
|
|
||||||
(**self).$trait_fn()
|
(**self).$trait_fn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + $Trait> $Trait for Box<T> {
|
impl<T: ?Sized + $Trait> $Trait for Box<T> {
|
||||||
type Output = T::Output;
|
fn $trait_fn(&self) -> Expr<$name> {
|
||||||
fn $trait_fn(&self) -> Self::Output {
|
|
||||||
(**self).$trait_fn()
|
(**self).$trait_fn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$($impl_trait $Trait for Expr<$name> {
|
$($impl_trait $Trait for Expr<$name> {
|
||||||
type Output = Expr<$name>;
|
fn $trait_fn(&self) -> Expr<$name> {
|
||||||
fn $trait_fn(&self) -> Self::Output {
|
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
})?
|
})?
|
||||||
|
|
@ -190,15 +171,13 @@ reset_type!(
|
||||||
);
|
);
|
||||||
|
|
||||||
impl ToSyncReset for bool {
|
impl ToSyncReset for bool {
|
||||||
type Output = SimValue<SyncReset>;
|
fn to_sync_reset(&self) -> Expr<SyncReset> {
|
||||||
fn to_sync_reset(&self) -> Self::Output {
|
self.to_expr().to_sync_reset()
|
||||||
SimValue::from_value(SyncReset, *self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToAsyncReset for bool {
|
impl ToAsyncReset for bool {
|
||||||
type Output = SimValue<AsyncReset>;
|
fn to_async_reset(&self) -> Expr<AsyncReset> {
|
||||||
fn to_async_reset(&self) -> Self::Output {
|
self.to_expr().to_async_reset()
|
||||||
SimValue::from_value(AsyncReset, *self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -2,13 +2,12 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::ValueType,
|
|
||||||
int::{BoolOrIntType, SInt, UInt},
|
int::{BoolOrIntType, SInt, UInt},
|
||||||
intern::{Intern, Interned, Memoize},
|
intern::{Intern, Interned, Memoize},
|
||||||
sim::interpreter::parts::{
|
sim::interpreter::parts::{
|
||||||
StateLayout, StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots,
|
StateLayout, StatePartIndex, StatePartKind, StatePartKindBigSlots, StatePartKindMemories,
|
||||||
StatePartKindMemories, StatePartKindSimOnlySlots, StatePartKindSmallSlots, StatePartLen,
|
StatePartKindSimOnlySlots, StatePartKindSmallSlots, StatePartLen, TypeIndexRange,
|
||||||
TypeIndexRange, TypeLayout, get_state_part_kinds,
|
TypeLayout, get_state_part_kinds,
|
||||||
},
|
},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
util::{HashMap, HashSet},
|
util::{HashMap, HashSet},
|
||||||
|
|
@ -17,11 +16,12 @@ use bitvec::slice::BitSlice;
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use num_traits::{One, Signed, ToPrimitive, Zero};
|
use num_traits::{One, Signed, ToPrimitive, Zero};
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::BorrowMut,
|
||||||
convert::Infallible,
|
convert::Infallible,
|
||||||
fmt::{self, Write},
|
fmt::{self, Write},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{ControlFlow, Deref, Index, IndexMut},
|
ops::{ControlFlow, Deref, DerefMut, Index, IndexMut},
|
||||||
};
|
};
|
||||||
use vec_map::VecMap;
|
use vec_map::VecMap;
|
||||||
|
|
||||||
|
|
@ -196,27 +196,13 @@ impl fmt::Debug for Insn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct PrefixLinesWrapper<'a, W> {
|
struct PrefixLinesWrapper<'a, W> {
|
||||||
writer: W,
|
writer: W,
|
||||||
at_beginning_of_line: bool,
|
at_beginning_of_line: bool,
|
||||||
blank_line_prefix: &'a str,
|
blank_line_prefix: &'a str,
|
||||||
line_prefix: &'a str,
|
line_prefix: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W> PrefixLinesWrapper<'a, W> {
|
|
||||||
pub(crate) fn new(writer: W, at_beginning_of_line: bool, line_prefix: &'a str) -> Self {
|
|
||||||
Self {
|
|
||||||
writer,
|
|
||||||
at_beginning_of_line,
|
|
||||||
blank_line_prefix: line_prefix.trim_end(),
|
|
||||||
line_prefix,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn into_inner(self) -> W {
|
|
||||||
self.writer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: fmt::Write> fmt::Write for PrefixLinesWrapper<'_, T> {
|
impl<T: fmt::Write> fmt::Write for PrefixLinesWrapper<'_, T> {
|
||||||
fn write_str(&mut self, input: &str) -> fmt::Result {
|
fn write_str(&mut self, input: &str) -> fmt::Result {
|
||||||
for part in input.split_inclusive('\n') {
|
for part in input.split_inclusive('\n') {
|
||||||
|
|
@ -253,7 +239,12 @@ impl Insn {
|
||||||
if fields.len() == 0 {
|
if fields.len() == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut f = PrefixLinesWrapper::new(f, false, " ");
|
let mut f = PrefixLinesWrapper {
|
||||||
|
writer: f,
|
||||||
|
at_beginning_of_line: false,
|
||||||
|
blank_line_prefix: "",
|
||||||
|
line_prefix: " ",
|
||||||
|
};
|
||||||
writeln!(f, " {{")?;
|
writeln!(f, " {{")?;
|
||||||
for (field_name, field) in fields {
|
for (field_name, field) in fields {
|
||||||
write!(f, "{field_name}: ")?;
|
write!(f, "{field_name}: ")?;
|
||||||
|
|
@ -329,7 +320,7 @@ impl Insn {
|
||||||
}
|
}
|
||||||
writeln!(f, ",")?;
|
writeln!(f, ",")?;
|
||||||
}
|
}
|
||||||
write!(f.into_inner(), "}}")
|
write!(f.writer, "}}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -914,21 +905,6 @@ impl<K: StatePartKind> StatePart<K> {
|
||||||
value: K::borrow_state(&mut self.value),
|
value: K::borrow_state(&mut self.value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn state_index_fetch_maybe_modified_flag(
|
|
||||||
&self,
|
|
||||||
part_index: StatePartIndex<K>,
|
|
||||||
) -> bool {
|
|
||||||
K::state_index_fetch_maybe_modified_flag(&self.value, part_index)
|
|
||||||
}
|
|
||||||
pub(crate) fn state_index_range_fetch_maybe_modified_flags(
|
|
||||||
&self,
|
|
||||||
part_index_range: StatePartIndexRange<K>,
|
|
||||||
) -> bool {
|
|
||||||
K::state_index_range_fetch_maybe_modified_flags(&self.value, part_index_range)
|
|
||||||
}
|
|
||||||
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
|
|
||||||
K::clear_all_maybe_modified_flags(&mut self.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
|
@ -936,38 +912,56 @@ pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> {
|
||||||
pub(crate) value: K::BorrowedState<'a>,
|
pub(crate) value: K::BorrowedState<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: StatePartKind> BorrowedStatePart<'_, K> {
|
impl<
|
||||||
|
'a,
|
||||||
|
K: StatePartKind<
|
||||||
|
BorrowedState<'a>: DerefMut<Target: IndexMut<usize, Output = T> + BorrowMut<[T]>>,
|
||||||
|
>,
|
||||||
|
T,
|
||||||
|
> BorrowedStatePart<'a, K>
|
||||||
|
{
|
||||||
pub(crate) fn get_disjoint_mut<const N: usize>(
|
pub(crate) fn get_disjoint_mut<const N: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
indexes: [StatePartIndex<K>; N],
|
indexes: [StatePartIndex<K>; N],
|
||||||
) -> [&mut K::StateElement; N] {
|
) -> [&mut T; N] {
|
||||||
K::borrowed_state_get_disjoint_mut(&mut self.value, indexes)
|
(*self.value)
|
||||||
|
.borrow_mut()
|
||||||
|
.get_disjoint_mut(indexes.map(|v| v.value as usize))
|
||||||
|
.expect("indexes are disjoint")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: StatePartKind> Index<StatePartIndex<K>> for StatePart<K> {
|
impl<K: StatePartKind<State: Deref<Target: Index<usize, Output = T>>>, T> Index<StatePartIndex<K>>
|
||||||
type Output = K::StateElement;
|
for StatePart<K>
|
||||||
|
{
|
||||||
|
type Output = T;
|
||||||
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
|
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
|
||||||
K::state_index(&self.value, index)
|
&self.value[index.value as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: StatePartKind> IndexMut<StatePartIndex<K>> for StatePart<K> {
|
impl<K: StatePartKind<State: DerefMut<Target: IndexMut<usize, Output = T>>>, T>
|
||||||
|
IndexMut<StatePartIndex<K>> for StatePart<K>
|
||||||
|
{
|
||||||
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
|
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
|
||||||
K::state_index_mut(&mut self.value, index)
|
&mut self.value[index.value as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: StatePartKind> Index<StatePartIndex<K>> for BorrowedStatePart<'_, K> {
|
impl<'a, K: StatePartKind<BorrowedState<'a>: Deref<Target: Index<usize, Output = T>>>, T>
|
||||||
type Output = K::StateElement;
|
Index<StatePartIndex<K>> for BorrowedStatePart<'a, K>
|
||||||
|
{
|
||||||
|
type Output = T;
|
||||||
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
|
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
|
||||||
K::borrowed_state_index(&self.value, index)
|
&self.value[index.value as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: StatePartKind> IndexMut<StatePartIndex<K>> for BorrowedStatePart<'_, K> {
|
impl<'a, K: StatePartKind<BorrowedState<'a>: DerefMut<Target: IndexMut<usize, Output = T>>>, T>
|
||||||
|
IndexMut<StatePartIndex<K>> for BorrowedStatePart<'a, K>
|
||||||
|
{
|
||||||
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
|
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
|
||||||
K::borrowed_state_index_mut(&mut self.value, index)
|
&mut self.value[index.value as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -980,7 +974,6 @@ macro_rules! make_state {
|
||||||
pub(crate) insns: Interned<Insns<InsnsBuildingDone>>,
|
pub(crate) insns: Interned<Insns<InsnsBuildingDone>>,
|
||||||
pub(crate) pc: usize,
|
pub(crate) pc: usize,
|
||||||
pub(crate) memory_write_log: Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
|
pub(crate) memory_write_log: Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
|
||||||
pub(crate) assert_failed_log: Vec<usize>,
|
|
||||||
$(pub(crate) $state_plural_field: StatePart<$state_kind>,)*
|
$(pub(crate) $state_plural_field: StatePart<$state_kind>,)*
|
||||||
$(pub(crate) $type_plural_field: StatePart<$type_kind>,)*
|
$(pub(crate) $type_plural_field: StatePart<$type_kind>,)*
|
||||||
}
|
}
|
||||||
|
|
@ -991,7 +984,6 @@ macro_rules! make_state {
|
||||||
insns: _,
|
insns: _,
|
||||||
pc,
|
pc,
|
||||||
memory_write_log,
|
memory_write_log,
|
||||||
assert_failed_log,
|
|
||||||
$($state_plural_field,)*
|
$($state_plural_field,)*
|
||||||
$($type_plural_field,)*
|
$($type_plural_field,)*
|
||||||
} = self;
|
} = self;
|
||||||
|
|
@ -999,7 +991,6 @@ macro_rules! make_state {
|
||||||
.field("insns", &InsnsOfState(self))
|
.field("insns", &InsnsOfState(self))
|
||||||
.field("pc", pc)
|
.field("pc", pc)
|
||||||
.field("memory_write_log", memory_write_log)
|
.field("memory_write_log", memory_write_log)
|
||||||
.field("assert_failed_log", assert_failed_log)
|
|
||||||
$(.field(stringify!($state_plural_field), $state_plural_field))*
|
$(.field(stringify!($state_plural_field), $state_plural_field))*
|
||||||
$(.field(stringify!($type_plural_field), $type_plural_field))*
|
$(.field(stringify!($type_plural_field), $type_plural_field))*
|
||||||
.finish()
|
.finish()
|
||||||
|
|
@ -1012,7 +1003,6 @@ macro_rules! make_state {
|
||||||
insns,
|
insns,
|
||||||
pc: 0,
|
pc: 0,
|
||||||
memory_write_log: Vec::with_capacity(32),
|
memory_write_log: Vec::with_capacity(32),
|
||||||
assert_failed_log: Vec::new(),
|
|
||||||
$($state_plural_field: StatePart::new(&insns.state_layout.$state_plural_field.layout_data),)*
|
$($state_plural_field: StatePart::new(&insns.state_layout.$state_plural_field.layout_data),)*
|
||||||
$($type_plural_field: StatePart::new(&insns.state_layout.ty.$type_plural_field.layout_data),)*
|
$($type_plural_field: StatePart::new(&insns.state_layout.ty.$type_plural_field.layout_data),)*
|
||||||
}
|
}
|
||||||
|
|
@ -1024,20 +1014,10 @@ macro_rules! make_state {
|
||||||
pc: self.pc,
|
pc: self.pc,
|
||||||
orig_pc: &mut self.pc,
|
orig_pc: &mut self.pc,
|
||||||
memory_write_log: &mut self.memory_write_log,
|
memory_write_log: &mut self.memory_write_log,
|
||||||
assert_failed_log: &mut self.assert_failed_log,
|
|
||||||
$($state_plural_field: self.$state_plural_field.borrow(),)*
|
$($state_plural_field: self.$state_plural_field.borrow(),)*
|
||||||
$($type_plural_field: self.$type_plural_field.borrow(),)*
|
$($type_plural_field: self.$type_plural_field.borrow(),)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn type_index_range_fetch_maybe_modified_flags(&self, range: TypeIndexRange) -> bool {
|
|
||||||
$(self.$type_plural_field.state_index_range_fetch_maybe_modified_flags(
|
|
||||||
range.$type_plural_field,
|
|
||||||
))||*
|
|
||||||
}
|
|
||||||
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
|
|
||||||
$(self.$state_plural_field.clear_all_maybe_modified_flags();)*
|
|
||||||
$(self.$type_plural_field.clear_all_maybe_modified_flags();)*
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -1047,7 +1027,6 @@ macro_rules! make_state {
|
||||||
pub(crate) orig_pc: &'a mut usize,
|
pub(crate) orig_pc: &'a mut usize,
|
||||||
pub(crate) pc: usize,
|
pub(crate) pc: usize,
|
||||||
pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
|
pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
|
||||||
pub(crate) assert_failed_log: &'a mut Vec<usize>,
|
|
||||||
$(pub(crate) $state_plural_field: BorrowedStatePart<'a, $state_kind>,)*
|
$(pub(crate) $state_plural_field: BorrowedStatePart<'a, $state_kind>,)*
|
||||||
$(pub(crate) $type_plural_field: BorrowedStatePart<'a, $type_kind>,)*
|
$(pub(crate) $type_plural_field: BorrowedStatePart<'a, $type_kind>,)*
|
||||||
}
|
}
|
||||||
|
|
@ -1305,7 +1284,6 @@ impl State {
|
||||||
insns: _,
|
insns: _,
|
||||||
pc,
|
pc,
|
||||||
memory_write_log: _,
|
memory_write_log: _,
|
||||||
assert_failed_log: _,
|
|
||||||
memories: _,
|
memories: _,
|
||||||
small_slots: _,
|
small_slots: _,
|
||||||
big_slots: _,
|
big_slots: _,
|
||||||
|
|
@ -1345,10 +1323,6 @@ impl BorrowedState<'_> {
|
||||||
self.memory_write_log.push(log_entry);
|
self.memory_write_log.push(log_entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cold]
|
|
||||||
fn assert_failed(&mut self, assert_index: usize) {
|
|
||||||
self.assert_failed_log.push(assert_index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bigint_pow2(width: usize) -> Interned<BigInt> {
|
fn bigint_pow2(width: usize) -> Interned<BigInt> {
|
||||||
|
|
@ -2116,19 +2090,6 @@ impl_insns! {
|
||||||
state.log_memory_write(memory, addr);
|
state.log_memory_write(memory, addr);
|
||||||
next!();
|
next!();
|
||||||
}
|
}
|
||||||
Assert {
|
|
||||||
#[kind = Input]
|
|
||||||
clk_triggered: StatePartIndex<StatePartKindSmallSlots>,
|
|
||||||
#[kind = Input]
|
|
||||||
pred: StatePartIndex<StatePartKindSmallSlots>,
|
|
||||||
#[kind = Immediate]
|
|
||||||
assert_index: usize,
|
|
||||||
} => {
|
|
||||||
if state.small_slots[clk_triggered] != 0 && state.small_slots[pred] == 0 {
|
|
||||||
state.assert_failed(assert_index);
|
|
||||||
}
|
|
||||||
next!();
|
|
||||||
}
|
|
||||||
Return => {
|
Return => {
|
||||||
break RunResult::Return(());
|
break RunResult::Return(());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
Insn, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, PrefixLinesWrapper,
|
Insn, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, PrefixLinesWrapper,
|
||||||
SmallSInt, SmallUInt, State,
|
SmallSInt, SmallUInt, State,
|
||||||
},
|
},
|
||||||
value::{DynSimOnly, DynSimOnlyValue},
|
value::{DynSimOnlyValue, DynSimOnly},
|
||||||
},
|
},
|
||||||
ty::CanonicalType,
|
ty::CanonicalType,
|
||||||
util::{chain, const_str_cmp},
|
util::{chain, const_str_cmp},
|
||||||
|
|
@ -236,7 +236,6 @@ pub(crate) trait StatePartKind:
|
||||||
type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy;
|
type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy;
|
||||||
type State: fmt::Debug + 'static + Clone;
|
type State: fmt::Debug + 'static + Clone;
|
||||||
type BorrowedState<'a>: 'a;
|
type BorrowedState<'a>: 'a;
|
||||||
type StateElement;
|
|
||||||
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State;
|
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State;
|
||||||
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>;
|
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>;
|
||||||
fn part_debug_data<BK: InsnsBuildingKind>(
|
fn part_debug_data<BK: InsnsBuildingKind>(
|
||||||
|
|
@ -248,35 +247,6 @@ pub(crate) trait StatePartKind:
|
||||||
index: StatePartIndex<Self>,
|
index: StatePartIndex<Self>,
|
||||||
f: &mut impl fmt::Write,
|
f: &mut impl fmt::Write,
|
||||||
) -> fmt::Result;
|
) -> fmt::Result;
|
||||||
fn state_index<'a>(
|
|
||||||
state: &'a Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement;
|
|
||||||
fn state_index_mut<'a>(
|
|
||||||
state: &'a mut Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement;
|
|
||||||
fn state_index_fetch_maybe_modified_flag(
|
|
||||||
state: &Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> bool;
|
|
||||||
fn state_index_range_fetch_maybe_modified_flags(
|
|
||||||
state: &Self::State,
|
|
||||||
part_index_range: StatePartIndexRange<Self>,
|
|
||||||
) -> bool;
|
|
||||||
fn clear_all_maybe_modified_flags(state: &mut Self::State);
|
|
||||||
fn borrowed_state_index<'a, 'b>(
|
|
||||||
state: &'a Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement;
|
|
||||||
fn borrowed_state_index_mut<'a, 'b>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement;
|
|
||||||
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_indexes: [StatePartIndex<Self>; N],
|
|
||||||
) -> [&'a mut Self::StateElement; N];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! make_state_part_kinds {
|
macro_rules! make_state_part_kinds {
|
||||||
|
|
@ -302,7 +272,6 @@ impl StatePartKind for StatePartKindMemories {
|
||||||
type LayoutData = MemoryData<Interned<BitSlice>>;
|
type LayoutData = MemoryData<Interned<BitSlice>>;
|
||||||
type State = Box<[MemoryData<BitBox>]>;
|
type State = Box<[MemoryData<BitBox>]>;
|
||||||
type BorrowedState<'a> = &'a mut [MemoryData<BitBox>];
|
type BorrowedState<'a> = &'a mut [MemoryData<BitBox>];
|
||||||
type StateElement = MemoryData<BitBox>;
|
|
||||||
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
|
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
|
||||||
layout_data
|
layout_data
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -328,95 +297,19 @@ impl StatePartKind for StatePartKindMemories {
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
write!(f, "{:#?}", &state.memories[index])
|
write!(f, "{:#?}", &state.memories[index])
|
||||||
}
|
}
|
||||||
fn state_index<'a>(
|
|
||||||
state: &'a Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement {
|
|
||||||
&state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_mut<'a>(
|
|
||||||
state: &'a mut Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement {
|
|
||||||
&mut state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_fetch_maybe_modified_flag(
|
|
||||||
_state: &Self::State,
|
|
||||||
_part_index: StatePartIndex<Self>,
|
|
||||||
) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
fn state_index_range_fetch_maybe_modified_flags(
|
|
||||||
_state: &Self::State,
|
|
||||||
part_index_range: StatePartIndexRange<Self>,
|
|
||||||
) -> bool {
|
|
||||||
part_index_range.len.value > 0
|
|
||||||
}
|
|
||||||
fn clear_all_maybe_modified_flags(_state: &mut Self::State) {}
|
|
||||||
fn borrowed_state_index<'a, 'b>(
|
|
||||||
state: &'a Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement {
|
|
||||||
&state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn borrowed_state_index_mut<'a, 'b>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement {
|
|
||||||
&mut state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_indexes: [StatePartIndex<Self>; N],
|
|
||||||
) -> [&'a mut Self::StateElement; N] {
|
|
||||||
state
|
|
||||||
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
|
|
||||||
.expect("indexes are disjoint")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
|
|
||||||
pub(crate) struct StateAndModified<T, M> {
|
|
||||||
pub(crate) state: T,
|
|
||||||
pub(crate) modified: M,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Deref<Target = [E]>, M: Deref<Target = [bool]>, E: fmt::Debug> fmt::Debug
|
|
||||||
for StateAndModified<T, M>
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_list()
|
|
||||||
.entries(self.state.iter().zip(self.modified.iter().copied()).map(
|
|
||||||
|(state, modified)| {
|
|
||||||
fmt::from_fn(move |f| {
|
|
||||||
state.fmt(f)?;
|
|
||||||
if modified {
|
|
||||||
f.write_str(" (modified)")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatePartKind for StatePartKindSmallSlots {
|
impl StatePartKind for StatePartKindSmallSlots {
|
||||||
const NAME: &'static str = "SmallSlots";
|
const NAME: &'static str = "SmallSlots";
|
||||||
type DebugData = SlotDebugData;
|
type DebugData = SlotDebugData;
|
||||||
type LayoutData = ();
|
type LayoutData = ();
|
||||||
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>;
|
type State = Box<[SmallUInt]>;
|
||||||
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>;
|
type BorrowedState<'a> = &'a mut [SmallUInt];
|
||||||
type StateElement = SmallUInt;
|
|
||||||
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
|
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
|
||||||
StateAndModified {
|
vec![0; layout_data.len()].into_boxed_slice()
|
||||||
state: vec![0; layout_data.len()].into_boxed_slice(),
|
|
||||||
modified: vec![false; layout_data.len()].into_boxed_slice(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
|
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
|
||||||
let StateAndModified { state, modified } = state;
|
state
|
||||||
StateAndModified { state, modified }
|
|
||||||
}
|
}
|
||||||
fn part_debug_data<BK: InsnsBuildingKind>(
|
fn part_debug_data<BK: InsnsBuildingKind>(
|
||||||
state_layout: &StateLayout<BK>,
|
state_layout: &StateLayout<BK>,
|
||||||
|
|
@ -437,80 +330,19 @@ impl StatePartKind for StatePartKindSmallSlots {
|
||||||
write!(f, "{value:#x} {}", value as SmallSInt)?;
|
write!(f, "{value:#x} {}", value as SmallSInt)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn state_index<'a>(
|
|
||||||
state: &'a Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement {
|
|
||||||
&state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_mut<'a>(
|
|
||||||
state: &'a mut Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
&mut state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_fetch_maybe_modified_flag(
|
|
||||||
state: &Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> bool {
|
|
||||||
state.modified[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_range_fetch_maybe_modified_flags(
|
|
||||||
state: &Self::State,
|
|
||||||
part_index_range: StatePartIndexRange<Self>,
|
|
||||||
) -> bool {
|
|
||||||
state.modified[part_index_range.start.as_usize()..]
|
|
||||||
[..part_index_range.len.as_index().as_usize()]
|
|
||||||
.contains(&true)
|
|
||||||
}
|
|
||||||
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
|
|
||||||
state.modified.fill(false);
|
|
||||||
}
|
|
||||||
fn borrowed_state_index<'a, 'b>(
|
|
||||||
state: &'a Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement {
|
|
||||||
&state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn borrowed_state_index_mut<'a, 'b>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
&mut state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_indexes: [StatePartIndex<Self>; N],
|
|
||||||
) -> [&'a mut Self::StateElement; N] {
|
|
||||||
for part_index in part_indexes {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
}
|
|
||||||
state
|
|
||||||
.state
|
|
||||||
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
|
|
||||||
.expect("indexes are disjoint")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatePartKind for StatePartKindBigSlots {
|
impl StatePartKind for StatePartKindBigSlots {
|
||||||
const NAME: &'static str = "BigSlots";
|
const NAME: &'static str = "BigSlots";
|
||||||
type DebugData = SlotDebugData;
|
type DebugData = SlotDebugData;
|
||||||
type LayoutData = ();
|
type LayoutData = ();
|
||||||
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>;
|
type State = Box<[BigInt]>;
|
||||||
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>;
|
type BorrowedState<'a> = &'a mut [BigInt];
|
||||||
type StateElement = BigInt;
|
|
||||||
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
|
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
|
||||||
let state: Box<[_]> = layout_data.iter().map(|_| BigInt::default()).collect();
|
layout_data.iter().map(|_| BigInt::default()).collect()
|
||||||
StateAndModified {
|
|
||||||
modified: vec![false; state.len()].into_boxed_slice(),
|
|
||||||
state,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
|
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
|
||||||
let StateAndModified { state, modified } = state;
|
state
|
||||||
StateAndModified { state, modified }
|
|
||||||
}
|
}
|
||||||
fn part_debug_data<BK: InsnsBuildingKind>(
|
fn part_debug_data<BK: InsnsBuildingKind>(
|
||||||
state_layout: &StateLayout<BK>,
|
state_layout: &StateLayout<BK>,
|
||||||
|
|
@ -529,80 +361,19 @@ impl StatePartKind for StatePartKindBigSlots {
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
write!(f, "{:#x}", state.big_slots[index])
|
write!(f, "{:#x}", state.big_slots[index])
|
||||||
}
|
}
|
||||||
fn state_index<'a>(
|
|
||||||
state: &'a Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement {
|
|
||||||
&state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_mut<'a>(
|
|
||||||
state: &'a mut Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
&mut state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_fetch_maybe_modified_flag(
|
|
||||||
state: &Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> bool {
|
|
||||||
state.modified[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_range_fetch_maybe_modified_flags(
|
|
||||||
state: &Self::State,
|
|
||||||
part_index_range: StatePartIndexRange<Self>,
|
|
||||||
) -> bool {
|
|
||||||
state.modified[part_index_range.start.as_usize()..]
|
|
||||||
[..part_index_range.len.as_index().as_usize()]
|
|
||||||
.contains(&true)
|
|
||||||
}
|
|
||||||
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
|
|
||||||
state.modified.fill(false);
|
|
||||||
}
|
|
||||||
fn borrowed_state_index<'a, 'b>(
|
|
||||||
state: &'a Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement {
|
|
||||||
&state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn borrowed_state_index_mut<'a, 'b>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
&mut state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_indexes: [StatePartIndex<Self>; N],
|
|
||||||
) -> [&'a mut Self::StateElement; N] {
|
|
||||||
for part_index in part_indexes {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
}
|
|
||||||
state
|
|
||||||
.state
|
|
||||||
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
|
|
||||||
.expect("indexes are disjoint")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatePartKind for StatePartKindSimOnlySlots {
|
impl StatePartKind for StatePartKindSimOnlySlots {
|
||||||
const NAME: &'static str = "SimOnlySlots";
|
const NAME: &'static str = "SimOnlySlots";
|
||||||
type DebugData = SlotDebugData;
|
type DebugData = SlotDebugData;
|
||||||
type LayoutData = DynSimOnly;
|
type LayoutData = DynSimOnly;
|
||||||
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>;
|
type State = Box<[DynSimOnlyValue]>;
|
||||||
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>;
|
type BorrowedState<'a> = &'a mut [DynSimOnlyValue];
|
||||||
type StateElement = DynSimOnlyValue;
|
|
||||||
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
|
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
|
||||||
let state: Box<[_]> = layout_data.iter().map(|ty| ty.default_value()).collect();
|
layout_data.iter().map(|ty| ty.default_value()).collect()
|
||||||
StateAndModified {
|
|
||||||
modified: vec![false; state.len()].into_boxed_slice(),
|
|
||||||
state,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
|
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
|
||||||
let StateAndModified { state, modified } = state;
|
state
|
||||||
StateAndModified { state, modified }
|
|
||||||
}
|
}
|
||||||
fn part_debug_data<BK: InsnsBuildingKind>(
|
fn part_debug_data<BK: InsnsBuildingKind>(
|
||||||
state_layout: &StateLayout<BK>,
|
state_layout: &StateLayout<BK>,
|
||||||
|
|
@ -621,61 +392,6 @@ impl StatePartKind for StatePartKindSimOnlySlots {
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
write!(f, "{:?}", state.sim_only_slots[index])
|
write!(f, "{:?}", state.sim_only_slots[index])
|
||||||
}
|
}
|
||||||
fn state_index<'a>(
|
|
||||||
state: &'a Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement {
|
|
||||||
&state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_mut<'a>(
|
|
||||||
state: &'a mut Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
&mut state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_fetch_maybe_modified_flag(
|
|
||||||
state: &Self::State,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> bool {
|
|
||||||
state.modified[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn state_index_range_fetch_maybe_modified_flags(
|
|
||||||
state: &Self::State,
|
|
||||||
part_index_range: StatePartIndexRange<Self>,
|
|
||||||
) -> bool {
|
|
||||||
state.modified[part_index_range.start.as_usize()..]
|
|
||||||
[..part_index_range.len.as_index().as_usize()]
|
|
||||||
.contains(&true)
|
|
||||||
}
|
|
||||||
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
|
|
||||||
state.modified.fill(false);
|
|
||||||
}
|
|
||||||
fn borrowed_state_index<'a, 'b>(
|
|
||||||
state: &'a Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a Self::StateElement {
|
|
||||||
&state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn borrowed_state_index_mut<'a, 'b>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_index: StatePartIndex<Self>,
|
|
||||||
) -> &'a mut Self::StateElement {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
&mut state.state[part_index.as_usize()]
|
|
||||||
}
|
|
||||||
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
|
|
||||||
state: &'a mut Self::BorrowedState<'b>,
|
|
||||||
part_indexes: [StatePartIndex<Self>; N],
|
|
||||||
) -> [&'a mut Self::StateElement; N] {
|
|
||||||
for part_index in part_indexes {
|
|
||||||
state.modified[part_index.as_usize()] = true;
|
|
||||||
}
|
|
||||||
state
|
|
||||||
.state
|
|
||||||
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
|
|
||||||
.expect("indexes are disjoint")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
|
@ -719,7 +435,12 @@ impl<K: StatePartKind> StatePartIndex<K> {
|
||||||
if state.is_some() || debug_data.is_some() {
|
if state.is_some() || debug_data.is_some() {
|
||||||
f.write_str(comment_start)?;
|
f.write_str(comment_start)?;
|
||||||
}
|
}
|
||||||
let mut f = PrefixLinesWrapper::new(f, false, comment_line_start);
|
let mut f = PrefixLinesWrapper {
|
||||||
|
writer: f,
|
||||||
|
at_beginning_of_line: false,
|
||||||
|
blank_line_prefix: comment_line_start.trim_end(),
|
||||||
|
line_prefix: comment_line_start,
|
||||||
|
};
|
||||||
if let Some(state) = state {
|
if let Some(state) = state {
|
||||||
f.write_str("(")?;
|
f.write_str("(")?;
|
||||||
K::debug_fmt_state_value(state, *self, &mut f)?;
|
K::debug_fmt_state_value(state, *self, &mut f)?;
|
||||||
|
|
@ -732,7 +453,7 @@ impl<K: StatePartKind> StatePartIndex<K> {
|
||||||
write!(f, "{debug_data:?}")?;
|
write!(f, "{debug_data:?}")?;
|
||||||
}
|
}
|
||||||
if state.is_some() || debug_data.is_some() {
|
if state.is_some() || debug_data.is_some() {
|
||||||
f.into_inner().write_str(comment_end)?;
|
f.writer.write_str(comment_end)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,32 +6,28 @@ use crate::{
|
||||||
bundle::{Bundle, BundleType},
|
bundle::{Bundle, BundleType},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
enum_::{Enum, EnumType},
|
enum_::{Enum, EnumType},
|
||||||
expr::{
|
expr::{CastBitsTo, Expr, ToExpr},
|
||||||
CastBitsTo, Expr, HdlPartialEq, HdlPartialEqImpl, HdlPartialOrd, ToExpr, ValueType,
|
|
||||||
value_category::{ValueCategorySimValue, ValueCategoryValue},
|
|
||||||
},
|
|
||||||
int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue},
|
int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue},
|
||||||
reset::{AsyncReset, Reset, SyncReset},
|
reset::{AsyncReset, Reset, SyncReset},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
ty::{
|
ty::{
|
||||||
CanonicalType, OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSlice,
|
CanonicalType, OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSlice,
|
||||||
OpaqueSimValueWriter, SimValueDebug, StaticType, Type, TypeProperties,
|
OpaqueSimValueWriter, StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
||||||
impl_match_variant_as_self,
|
|
||||||
},
|
},
|
||||||
util::{
|
util::{
|
||||||
ConstUsize,
|
ConstUsize, HashMap,
|
||||||
alternating_cell::{AlternatingCell, AlternatingCellMethods},
|
alternating_cell::{AlternatingCell, AlternatingCellMethods},
|
||||||
serde_by_id::{SerdeById, SerdeByIdProperties, SerdeByIdTable, SerdeByIdTrait},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bitvec::{slice::BitSlice, vec::BitVec};
|
use bitvec::{slice::BitSlice, vec::BitVec};
|
||||||
|
use hashbrown::hash_map::Entry;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::{Borrow, BorrowMut, Cow},
|
borrow::Cow,
|
||||||
fmt,
|
fmt::{self, Write},
|
||||||
num::NonZero,
|
hash::{BuildHasher, Hash, Hasher, RandomState},
|
||||||
ops::{Deref, DerefMut, Index, IndexMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::Arc,
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) mod sim_only_value_unsafe;
|
pub(crate) mod sim_only_value_unsafe;
|
||||||
|
|
@ -140,7 +136,7 @@ impl<T: Type<SimValue: Serialize> + Serialize> Serialize for SimValue<T> {
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
SerdeSimValue {
|
SerdeSimValue {
|
||||||
ty: self.ty(),
|
ty: SimValue::ty(self),
|
||||||
value: std::borrow::Cow::Borrowed(&*self),
|
value: std::borrow::Cow::Borrowed(&*self),
|
||||||
}
|
}
|
||||||
.serialize(serializer)
|
.serialize(serializer)
|
||||||
|
|
@ -161,22 +157,7 @@ pub struct SimValue<T: Type> {
|
||||||
inner: AlternatingCell<SimValueInner<T>>,
|
inner: AlternatingCell<SimValueInner<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type> ValueType for SimValue<T> {
|
impl<T: Type + Clone> Clone for SimValue<T> {
|
||||||
type Type = T;
|
|
||||||
type ValueCategory = ValueCategorySimValue;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
self.inner.share().ty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type<SimValue: fmt::Display>> fmt::Display for SimValue<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
T::SimValue::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type> Clone for SimValue<T> {
|
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: AlternatingCell::new_unique(self.inner.share().clone()),
|
inner: AlternatingCell::new_unique(self.inner.share().clone()),
|
||||||
|
|
@ -231,6 +212,9 @@ impl<T: Type> SimValue<T> {
|
||||||
inner: AlternatingCell::new_unique(inner),
|
inner: AlternatingCell::new_unique(inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn ty(this: &Self) -> T {
|
||||||
|
this.inner.share().ty
|
||||||
|
}
|
||||||
pub fn into_opaque(this: Self) -> OpaqueSimValue {
|
pub fn into_opaque(this: Self) -> OpaqueSimValue {
|
||||||
this.inner.into_inner().into_opaque()
|
this.inner.into_inner().into_opaque()
|
||||||
}
|
}
|
||||||
|
|
@ -269,7 +253,7 @@ impl<T: Type> SimValue<T> {
|
||||||
SimValue::from_opaque(ty.canonical(), opaque)
|
SimValue::from_opaque(ty.canonical(), opaque)
|
||||||
}
|
}
|
||||||
pub fn canonical(this: &Self) -> SimValue<CanonicalType> {
|
pub fn canonical(this: &Self) -> SimValue<CanonicalType> {
|
||||||
SimValue::from_opaque(this.ty().canonical(), Self::opaque(this).clone())
|
SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone())
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self
|
pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self
|
||||||
|
|
@ -290,7 +274,7 @@ impl<T: Type> SimValue<T> {
|
||||||
where
|
where
|
||||||
T: IntType,
|
T: IntType,
|
||||||
{
|
{
|
||||||
SimValue::from_opaque(this.ty().as_dyn_int(), Self::opaque(&this).clone())
|
SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone())
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn from_bundle(v: SimValue<Bundle>) -> Self
|
pub fn from_bundle(v: SimValue<Bundle>) -> Self
|
||||||
|
|
@ -312,7 +296,7 @@ impl<T: Type> SimValue<T> {
|
||||||
T: BundleType,
|
T: BundleType,
|
||||||
{
|
{
|
||||||
SimValue::from_opaque(
|
SimValue::from_opaque(
|
||||||
Bundle::from_canonical(this.ty().canonical()),
|
Bundle::from_canonical(Self::ty(this).canonical()),
|
||||||
Self::opaque(&this).clone(),
|
Self::opaque(&this).clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +320,7 @@ impl<T: Type> SimValue<T> {
|
||||||
T: EnumType,
|
T: EnumType,
|
||||||
{
|
{
|
||||||
SimValue::from_opaque(
|
SimValue::from_opaque(
|
||||||
Enum::from_canonical(this.ty().canonical()),
|
Enum::from_canonical(Self::ty(this).canonical()),
|
||||||
Self::opaque(&this).clone(),
|
Self::opaque(&this).clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -358,11 +342,17 @@ impl<T: Type> DerefMut for SimValue<T> {
|
||||||
|
|
||||||
impl<T: Type> fmt::Debug for SimValue<T> {
|
impl<T: Type> fmt::Debug for SimValue<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
T::SimValue::fmt(&**self, f)
|
let inner = self.inner.share();
|
||||||
|
f.debug_struct("SimValue")
|
||||||
|
.field("ty", &inner.ty)
|
||||||
|
.field("value", &inner.value)
|
||||||
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type> ToExpr for SimValue<T> {
|
impl<T: Type> ToExpr for SimValue<T> {
|
||||||
|
type Type = T;
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_expr(&self) -> Expr<Self::Type> {
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
let inner = self.inner.share();
|
let inner = self.inner.share();
|
||||||
|
|
@ -370,303 +360,43 @@ impl<T: Type> ToExpr for SimValue<T> {
|
||||||
inner.sim_only_values_len, 0,
|
inner.sim_only_values_len, 0,
|
||||||
"can't convert sim-only values to Expr"
|
"can't convert sim-only values to Expr"
|
||||||
);
|
);
|
||||||
inner.opaque.bits().to_expr().cast_bits_to(inner.ty)
|
inner.opaque.bits().cast_bits_to(inner.ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type, Len: Size, I> Index<I> for SimValue<ArrayType<T, Len>>
|
pub trait SimValuePartialEq<T: Type = Self>: Type {
|
||||||
where
|
#[track_caller]
|
||||||
[SimValue<T>]: Index<I>,
|
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<T>) -> bool;
|
||||||
{
|
|
||||||
type Output = <[SimValue<T>] as Index<I>>::Output;
|
|
||||||
|
|
||||||
fn index(&self, index: I) -> &Self::Output {
|
|
||||||
(**self).borrow().index(index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type, Len: Size, I> IndexMut<I> for SimValue<ArrayType<T, Len>>
|
impl<T: SimValuePartialEq<U>, U: Type> PartialEq<SimValue<U>> for SimValue<T> {
|
||||||
where
|
|
||||||
[SimValue<T>]: IndexMut<I>,
|
|
||||||
{
|
|
||||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
|
||||||
(**self).borrow_mut().index_mut(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, Len: Size> SimValue<ArrayType<T, Len>> {
|
|
||||||
pub fn iter(&self) -> std::slice::Iter<'_, SimValue<T>> {
|
|
||||||
(**self).borrow().iter()
|
|
||||||
}
|
|
||||||
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, SimValue<T>> {
|
|
||||||
(**self).borrow_mut().iter_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, Len: Size> IntoIterator for SimValue<ArrayType<T, Len>> {
|
|
||||||
type Item = SimValue<T>;
|
|
||||||
type IntoIter = std::vec::IntoIter<SimValue<T>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
Vec::into_iter(Self::into_value(self).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Type, Len: Size> IntoIterator for &'a SimValue<ArrayType<T, Len>> {
|
|
||||||
type Item = &'a SimValue<T>;
|
|
||||||
type IntoIter = std::slice::Iter<'a, SimValue<T>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Type, Len: Size> IntoIterator for &'a mut SimValue<ArrayType<T, Len>> {
|
|
||||||
type Item = &'a mut SimValue<T>;
|
|
||||||
type IntoIter = std::slice::IterMut<'a, SimValue<T>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.iter_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, Len: Size> AsRef<[SimValue<T>]> for SimValue<ArrayType<T, Len>> {
|
|
||||||
fn as_ref(&self) -> &[SimValue<T>] {
|
|
||||||
(**self).as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, Len: Size> AsMut<[SimValue<T>]> for SimValue<ArrayType<T, Len>> {
|
|
||||||
fn as_mut(&mut self) -> &mut [SimValue<T>] {
|
|
||||||
(**self).as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, U: Type> PartialEq<SimValue<U>> for SimValue<T>
|
|
||||||
where
|
|
||||||
Self: for<'a> HdlPartialEq<&'a SimValue<U>, Output = SimValue<Bool>>,
|
|
||||||
{
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn eq(&self, other: &SimValue<U>) -> bool {
|
fn eq(&self, other: &SimValue<U>) -> bool {
|
||||||
*self.cmp_eq(other)
|
T::sim_value_eq(self, other)
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn ne(&self, other: &SimValue<U>) -> bool {
|
|
||||||
*self.cmp_ne(other)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SimValueEq: Type
|
impl<Width: Size> SimValuePartialEq<Self> for UIntType<Width> {
|
||||||
where
|
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
|
||||||
for<'a> SimValue<Self>: HdlPartialEq<&'a SimValue<Self>, Output = SimValue<Bool>>,
|
**this == **other
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimValueEq> Eq for SimValue<T> where
|
|
||||||
for<'a> SimValue<T>: HdlPartialEq<&'a SimValue<T>, Output = SimValue<Bool>>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, U: Type> PartialOrd<SimValue<U>> for SimValue<T>
|
|
||||||
where
|
|
||||||
Self: for<'a> HdlPartialOrd<&'a SimValue<U>, Output = SimValue<Bool>>,
|
|
||||||
{
|
|
||||||
#[track_caller]
|
|
||||||
fn partial_cmp(&self, other: &SimValue<U>) -> Option<std::cmp::Ordering> {
|
|
||||||
if *self.cmp_eq(other) {
|
|
||||||
Some(std::cmp::Ordering::Equal)
|
|
||||||
} else if *self.cmp_lt(other) {
|
|
||||||
Some(std::cmp::Ordering::Less)
|
|
||||||
} else if *self.cmp_gt(other) {
|
|
||||||
Some(std::cmp::Ordering::Greater)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn lt(&self, other: &SimValue<U>) -> bool {
|
|
||||||
*self.cmp_lt(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn le(&self, other: &SimValue<U>) -> bool {
|
|
||||||
*self.cmp_le(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn gt(&self, other: &SimValue<U>) -> bool {
|
|
||||||
*self.cmp_gt(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn ge(&self, other: &SimValue<U>) -> bool {
|
|
||||||
*self.cmp_ge(other)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SimValueOrd: SimValueEq
|
impl<Width: Size> SimValuePartialEq<Self> for SIntType<Width> {
|
||||||
where
|
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
|
||||||
for<'a> SimValue<Self>: HdlPartialOrd<&'a SimValue<Self>, Output = SimValue<Bool>>,
|
**this == **other
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimValueOrd> Ord for SimValue<T>
|
|
||||||
where
|
|
||||||
for<'a> SimValue<T>: HdlPartialOrd<&'a SimValue<T>, Output = SimValue<Bool>>,
|
|
||||||
{
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
if *self.cmp_eq(other) {
|
|
||||||
std::cmp::Ordering::Equal
|
|
||||||
} else if *self.cmp_lt(other) {
|
|
||||||
std::cmp::Ordering::Less
|
|
||||||
} else {
|
|
||||||
std::cmp::Ordering::Greater
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Width: Size> SimValueEq for UIntType<Width> {}
|
impl SimValuePartialEq<Bool> for Bool {
|
||||||
|
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Bool>) -> bool {
|
||||||
impl<Width: Size> SimValueOrd for UIntType<Width> {}
|
**this == **other
|
||||||
|
}
|
||||||
impl<Width: Size> SimValueEq for SIntType<Width> {}
|
|
||||||
|
|
||||||
impl<Width: Size> SimValueOrd for SIntType<Width> {}
|
|
||||||
|
|
||||||
macro_rules! impl_sim_value_cmp_as_bool {
|
|
||||||
($ty:ident) => {
|
|
||||||
impl SimValueEq for $ty {}
|
|
||||||
|
|
||||||
impl SimValueOrd for $ty {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_sim_value_cmp_as_bool!(Bool);
|
pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> {
|
||||||
impl_sim_value_cmp_as_bool!(Clock);
|
type Type: Type;
|
||||||
impl_sim_value_cmp_as_bool!(Reset);
|
|
||||||
impl_sim_value_cmp_as_bool!(SyncReset);
|
|
||||||
impl_sim_value_cmp_as_bool!(AsyncReset);
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub mod match_sim_value {
|
|
||||||
use crate::{sim::value::SimValue, ty::Type};
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
macro_rules! wrapper {
|
|
||||||
(
|
|
||||||
$(pub struct $wrapper:ident<$T:ident>($inner:ty);)*
|
|
||||||
) => {
|
|
||||||
$(#[doc(hidden)]
|
|
||||||
pub struct $wrapper<$T>($inner);
|
|
||||||
|
|
||||||
impl<$T> $wrapper<$T> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn new(value: $T) -> Self {
|
|
||||||
Self(<$inner>::new(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$T> Deref for $wrapper<$T> {
|
|
||||||
type Target = $inner;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$T> DerefMut for $wrapper<$T> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
})*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper! {
|
|
||||||
pub struct MatchSimValueHelperCheckSimValue<T>(MatchSimValueHelperCheckMutSimValue<T>);
|
|
||||||
pub struct MatchSimValueHelperCheckMutSimValue<T>(MatchSimValueHelperCheckRefSimValue<T>);
|
|
||||||
pub struct MatchSimValueHelperCheckRefSimValue<T>(MatchSimValueHelperCheckRefRefSimValue<T>);
|
|
||||||
pub struct MatchSimValueHelperCheckRefRefSimValue<T>(MatchSimValueHelperCheckRefMutSimValue<T>);
|
|
||||||
pub struct MatchSimValueHelperCheckRefMutSimValue<T>(MatchSimValueHelperCheckMutRefSimValue<T>);
|
|
||||||
pub struct MatchSimValueHelperCheckMutRefSimValue<T>(MatchSimValueHelperCheckMutMutSimValue<T>);
|
|
||||||
pub struct MatchSimValueHelperCheckMutMutSimValue<T>(MatchSimValueHelperIdentity<T>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type> MatchSimValueHelperCheckSimValue<SimValue<T>> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn __fayalite_match_sim_value(&mut self) -> T::SimValue {
|
|
||||||
SimValue::into_value(self.take())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Type> MatchSimValueHelperCheckMutSimValue<&'a mut SimValue<T>> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn __fayalite_match_sim_value(&mut self) -> &'a mut T::SimValue {
|
|
||||||
self.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Type> MatchSimValueHelperCheckRefSimValue<&'a SimValue<T>> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn __fayalite_match_sim_value(&mut self) -> &'a T::SimValue {
|
|
||||||
self.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, T: Type> MatchSimValueHelperCheckRefRefSimValue<&'a &'b SimValue<T>> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn __fayalite_match_sim_value(&mut self) -> &'b T::SimValue {
|
|
||||||
self.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, T: Type> MatchSimValueHelperCheckRefMutSimValue<&'a &'b mut SimValue<T>> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn __fayalite_match_sim_value(&mut self) -> &'a T::SimValue {
|
|
||||||
self.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, T: Type> MatchSimValueHelperCheckMutRefSimValue<&'a mut &'b SimValue<T>> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn __fayalite_match_sim_value(&mut self) -> &'b T::SimValue {
|
|
||||||
self.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, T: Type> MatchSimValueHelperCheckMutMutSimValue<&'a mut &'b mut SimValue<T>> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn __fayalite_match_sim_value(&mut self) -> &'a mut T::SimValue {
|
|
||||||
self.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub struct MatchSimValueHelperIdentity<T>(Option<T>);
|
|
||||||
|
|
||||||
impl<T> MatchSimValueHelperIdentity<T> {
|
|
||||||
fn new(v: T) -> Self {
|
|
||||||
Self(Some(v))
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
fn take(&mut self) -> T {
|
|
||||||
self.0.take().expect("known to be Some")
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn __fayalite_match_sim_value(&mut self) -> T {
|
|
||||||
self.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub type MatchSimValueHelper<T> = MatchSimValueHelperCheckSimValue<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ToSimValue: ToSimValueWithType<<Self as ValueType>::Type> + ValueType {
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type>;
|
fn to_sim_value(&self) -> SimValue<Self::Type>;
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
|
@ -708,31 +438,31 @@ pub trait ToSimValueWithType<T: Type> {
|
||||||
|
|
||||||
macro_rules! forward_to_sim_value_with_type {
|
macro_rules! forward_to_sim_value_with_type {
|
||||||
([$($generics:tt)*] $ty:ty) => {
|
([$($generics:tt)*] $ty:ty) => {
|
||||||
impl<$($generics)*> ToSimValueWithType<<Self as ValueType>::Type> for $ty {
|
impl<$($generics)*> ToSimValueWithType<<Self as ToSimValue>::Type> for $ty {
|
||||||
fn to_sim_value_with_type(&self, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
|
fn to_sim_value_with_type(&self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
|
||||||
let retval = Self::to_sim_value(self);
|
let retval = Self::to_sim_value(self);
|
||||||
assert_eq!(retval.ty(), ty);
|
assert_eq!(SimValue::ty(&retval), ty);
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn into_sim_value_with_type(self, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type>
|
fn into_sim_value_with_type(self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let retval = Self::into_sim_value(self);
|
let retval = Self::into_sim_value(self);
|
||||||
assert_eq!(retval.ty(), ty);
|
assert_eq!(SimValue::ty(&retval), ty);
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
|
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
|
||||||
let retval = Self::arc_into_sim_value(self);
|
let retval = Self::arc_into_sim_value(self);
|
||||||
assert_eq!(retval.ty(), ty);
|
assert_eq!(SimValue::ty(&retval), ty);
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
|
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
|
||||||
let retval = Self::arc_to_sim_value(self);
|
let retval = Self::arc_to_sim_value(self);
|
||||||
assert_eq!(retval.ty(), ty);
|
assert_eq!(SimValue::ty(&retval), ty);
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -740,6 +470,7 @@ macro_rules! forward_to_sim_value_with_type {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type> ToSimValue for SimValue<T> {
|
impl<T: Type> ToSimValue for SimValue<T> {
|
||||||
|
type Type = T;
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
self.clone()
|
self.clone()
|
||||||
}
|
}
|
||||||
|
|
@ -795,7 +526,9 @@ impl<T: Type> ToSimValueWithType<T> for BitSlice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This {
|
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ This {
|
||||||
|
type Type = This::Type;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
This::to_sim_value(self)
|
This::to_sim_value(self)
|
||||||
}
|
}
|
||||||
|
|
@ -808,6 +541,8 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ mut This {
|
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ mut This {
|
||||||
|
type Type = This::Type;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
This::to_sim_value(self)
|
This::to_sim_value(self)
|
||||||
}
|
}
|
||||||
|
|
@ -820,6 +555,8 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<This: ?Sized + ToSimValue> ToSimValue for Arc<This> {
|
impl<This: ?Sized + ToSimValue> ToSimValue for Arc<This> {
|
||||||
|
type Type = This::Type;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
This::arc_to_sim_value(self)
|
This::arc_to_sim_value(self)
|
||||||
}
|
}
|
||||||
|
|
@ -840,6 +577,7 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for Ar
|
||||||
impl<This: ?Sized + ToSimValue + Send + Sync + 'static> ToSimValue
|
impl<This: ?Sized + ToSimValue + Send + Sync + 'static> ToSimValue
|
||||||
for crate::intern::Interned<This>
|
for crate::intern::Interned<This>
|
||||||
{
|
{
|
||||||
|
type Type = This::Type;
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
This::to_sim_value(self)
|
This::to_sim_value(self)
|
||||||
}
|
}
|
||||||
|
|
@ -854,6 +592,8 @@ impl<This: ?Sized + ToSimValueWithType<T> + Send + Sync + 'static, T: Type> ToSi
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<This: ToSimValue> ToSimValue for Box<This> {
|
impl<This: ToSimValue> ToSimValue for Box<This> {
|
||||||
|
type Type = This::Type;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
This::to_sim_value(self)
|
This::to_sim_value(self)
|
||||||
}
|
}
|
||||||
|
|
@ -896,6 +636,8 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for [
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for [Element] {
|
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for [Element] {
|
||||||
|
type Type = Array<Element::Type>;
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
||||||
|
|
@ -931,6 +673,8 @@ impl<Element: ToSimValue<Type: StaticType>, const N: usize> ToSimValue for [Elem
|
||||||
where
|
where
|
||||||
ConstUsize<N>: KnownSize,
|
ConstUsize<N>: KnownSize,
|
||||||
{
|
{
|
||||||
|
type Type = Array<Element::Type, N>;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_array_elements(StaticType::TYPE, self)
|
SimValue::from_array_elements(StaticType::TYPE, self)
|
||||||
}
|
}
|
||||||
|
|
@ -984,6 +728,8 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for V
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Vec<Element> {
|
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Vec<Element> {
|
||||||
|
type Type = Array<Element::Type>;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
||||||
}
|
}
|
||||||
|
|
@ -1024,6 +770,8 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for B
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Box<[Element]> {
|
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Box<[Element]> {
|
||||||
|
type Type = Array<Element::Type>;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
||||||
}
|
}
|
||||||
|
|
@ -1053,10 +801,11 @@ impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalTyp
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type> ToSimValue for Expr<T> {
|
impl<T: Type> ToSimValue for Expr<T> {
|
||||||
|
type Type = T;
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_bitslice(
|
SimValue::from_bitslice(
|
||||||
self.ty(),
|
Expr::ty(*self),
|
||||||
&crate::expr::ToLiteralBits::to_literal_bits(self)
|
&crate::expr::ToLiteralBits::to_literal_bits(self)
|
||||||
.expect("must be a literal expression"),
|
.expect("must be a literal expression"),
|
||||||
)
|
)
|
||||||
|
|
@ -1076,6 +825,8 @@ macro_rules! impl_to_sim_value_for_bool_like {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToSimValue for bool {
|
impl ToSimValue for bool {
|
||||||
|
type Type = Bool;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_value(Bool, *self)
|
SimValue::from_value(Bool, *self)
|
||||||
}
|
}
|
||||||
|
|
@ -1097,8 +848,7 @@ impl ToSimValueWithType<CanonicalType> for bool {
|
||||||
| CanonicalType::Enum(_)
|
| CanonicalType::Enum(_)
|
||||||
| CanonicalType::Bundle(_)
|
| CanonicalType::Bundle(_)
|
||||||
| CanonicalType::PhantomConst(_)
|
| CanonicalType::PhantomConst(_)
|
||||||
| CanonicalType::DynSimOnly(_)
|
| CanonicalType::DynSimOnly(_) => {
|
||||||
| CanonicalType::TraceAsString(_) => {
|
|
||||||
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
|
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
|
||||||
}
|
}
|
||||||
CanonicalType::Bool(_)
|
CanonicalType::Bool(_)
|
||||||
|
|
@ -1114,8 +864,10 @@ impl ToSimValueWithType<CanonicalType> for bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_to_sim_value_for_primitive_int {
|
macro_rules! impl_to_sim_value_for_primitive_int {
|
||||||
($prim:ty) => {
|
($prim:ident) => {
|
||||||
impl ToSimValue for $prim {
|
impl ToSimValue for $prim {
|
||||||
|
type Type = <$prim as ToExpr>::Type;
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_sim_value(
|
fn to_sim_value(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -1126,15 +878,15 @@ macro_rules! impl_to_sim_value_for_primitive_int {
|
||||||
|
|
||||||
forward_to_sim_value_with_type!([] $prim);
|
forward_to_sim_value_with_type!([] $prim);
|
||||||
|
|
||||||
impl ToSimValueWithType<<<$prim as ValueType>::Type as IntType>::Dyn> for $prim {
|
impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_sim_value_with_type(
|
fn to_sim_value_with_type(
|
||||||
&self,
|
&self,
|
||||||
ty: <<$prim as ValueType>::Type as IntType>::Dyn,
|
ty: <<$prim as ToExpr>::Type as IntType>::Dyn,
|
||||||
) -> SimValue<<<$prim as ValueType>::Type as IntType>::Dyn> {
|
) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> {
|
||||||
SimValue::from_value(
|
SimValue::from_value(
|
||||||
ty,
|
ty,
|
||||||
<<$prim as ValueType>::Type as Type>::SimValue::from(*self).as_dyn_int(),
|
<<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1142,7 +894,7 @@ macro_rules! impl_to_sim_value_for_primitive_int {
|
||||||
impl ToSimValueWithType<CanonicalType> for $prim {
|
impl ToSimValueWithType<CanonicalType> for $prim {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||||
let ty: <<$prim as ValueType>::Type as IntType>::Dyn = Type::from_canonical(ty);
|
let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty);
|
||||||
SimValue::into_canonical(self.to_sim_value_with_type(ty))
|
SimValue::into_canonical(self.to_sim_value_with_type(ty))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1161,22 +913,12 @@ impl_to_sim_value_for_primitive_int!(i32);
|
||||||
impl_to_sim_value_for_primitive_int!(i64);
|
impl_to_sim_value_for_primitive_int!(i64);
|
||||||
impl_to_sim_value_for_primitive_int!(i128);
|
impl_to_sim_value_for_primitive_int!(i128);
|
||||||
impl_to_sim_value_for_primitive_int!(isize);
|
impl_to_sim_value_for_primitive_int!(isize);
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<u8>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<u16>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<u32>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<u64>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<u128>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<usize>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<i8>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<i16>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<i32>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<i64>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<i128>);
|
|
||||||
impl_to_sim_value_for_primitive_int!(NonZero<isize>);
|
|
||||||
|
|
||||||
macro_rules! impl_to_sim_value_for_int_value {
|
macro_rules! impl_to_sim_value_for_int_value {
|
||||||
($IntValue:ident, $Int:ident, $IntType:ident) => {
|
($IntValue:ident, $Int:ident, $IntType:ident) => {
|
||||||
impl<Width: Size> ToSimValue for $IntValue<Width> {
|
impl<Width: Size> ToSimValue for $IntValue<Width> {
|
||||||
|
type Type = $IntType<Width>;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_value(self.ty(), self.clone())
|
SimValue::from_value(self.ty(), self.clone())
|
||||||
}
|
}
|
||||||
|
|
@ -1227,17 +969,80 @@ macro_rules! impl_to_sim_value_for_int_value {
|
||||||
impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType);
|
impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType);
|
||||||
impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType);
|
impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType);
|
||||||
|
|
||||||
impl SerdeByIdTrait for DynSimOnly {
|
#[derive(Default)]
|
||||||
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Self> {
|
struct DynSimOnlySerdeTableRest {
|
||||||
self.serde_by_id_properties_inner()
|
from_serde: HashMap<DynSimOnlySerdeId, DynSimOnly>,
|
||||||
}
|
serde_id_random_state: RandomState,
|
||||||
|
buffer: String,
|
||||||
|
}
|
||||||
|
|
||||||
fn static_table() -> &'static SerdeByIdTable<Self> {
|
impl DynSimOnlySerdeTableRest {
|
||||||
static TABLE: SerdeByIdTable<DynSimOnly> = SerdeByIdTable::new();
|
#[cold]
|
||||||
&TABLE
|
fn add_new(&mut self, ty: DynSimOnly) -> DynSimOnlySerdeId {
|
||||||
|
let mut try_number = 0u64;
|
||||||
|
let mut hasher = self.serde_id_random_state.build_hasher();
|
||||||
|
// extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits
|
||||||
|
write!(self.buffer, "{:?}", ty.type_id()).expect("shouldn't ever fail");
|
||||||
|
self.buffer.hash(&mut hasher);
|
||||||
|
loop {
|
||||||
|
let mut hasher = hasher.clone();
|
||||||
|
try_number.hash(&mut hasher);
|
||||||
|
try_number += 1;
|
||||||
|
let retval = DynSimOnlySerdeId(std::array::from_fn(|i| {
|
||||||
|
let mut hasher = hasher.clone();
|
||||||
|
i.hash(&mut hasher);
|
||||||
|
hasher.finish() as u32
|
||||||
|
}));
|
||||||
|
match self.from_serde.entry(retval) {
|
||||||
|
Entry::Occupied(_) => continue,
|
||||||
|
Entry::Vacant(e) => {
|
||||||
|
e.insert(ty);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const NAME: &'static str = "DynSimOnly";
|
#[derive(Default)]
|
||||||
|
struct DynSimOnlySerdeTable {
|
||||||
|
to_serde: HashMap<DynSimOnly, DynSimOnlySerdeId>,
|
||||||
|
rest: DynSimOnlySerdeTableRest,
|
||||||
|
}
|
||||||
|
|
||||||
|
static DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE: Mutex<Option<DynSimOnlySerdeTable>> = Mutex::new(None);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct DynSimOnlySerdeId([u32; 4]);
|
||||||
|
|
||||||
|
impl From<DynSimOnly> for DynSimOnlySerdeId {
|
||||||
|
fn from(ty: DynSimOnly) -> Self {
|
||||||
|
let mut locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE
|
||||||
|
.lock()
|
||||||
|
.expect("shouldn't be poison");
|
||||||
|
let DynSimOnlySerdeTable { to_serde, rest } = locked.get_or_insert_default();
|
||||||
|
match to_serde.entry(ty) {
|
||||||
|
Entry::Occupied(occupied_entry) => *occupied_entry.get(),
|
||||||
|
Entry::Vacant(vacant_entry) => *vacant_entry.insert(rest.add_new(ty)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynSimOnlySerdeId {
|
||||||
|
fn ty(self) -> Option<DynSimOnly> {
|
||||||
|
let locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE
|
||||||
|
.lock()
|
||||||
|
.expect("shouldn't be poison");
|
||||||
|
Some(*locked.as_ref()?.rest.from_serde.get(&self)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
|
||||||
|
struct DynSimOnlySerde<'a> {
|
||||||
|
random_id: DynSimOnlySerdeId,
|
||||||
|
#[serde(borrow)]
|
||||||
|
type_name: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for DynSimOnly {
|
impl Serialize for DynSimOnly {
|
||||||
|
|
@ -1245,7 +1050,11 @@ impl Serialize for DynSimOnly {
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
SerdeById { inner: *self }.serialize(serializer)
|
DynSimOnlySerde {
|
||||||
|
random_id: (*self).into(),
|
||||||
|
type_name: Cow::Borrowed(self.type_name()),
|
||||||
|
}
|
||||||
|
.serialize(serializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1254,7 +1063,16 @@ impl<'de> Deserialize<'de> for DynSimOnly {
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
Ok(SerdeById::deserialize(deserializer)?.inner)
|
let deserialized = DynSimOnlySerde::deserialize(deserializer)?;
|
||||||
|
let retval = deserialized
|
||||||
|
.random_id
|
||||||
|
.ty()
|
||||||
|
.filter(|ty| ty.type_name() == deserialized.type_name);
|
||||||
|
retval.ok_or_else(|| {
|
||||||
|
D::Error::custom(
|
||||||
|
"doesn't match any DynSimOnly that was serialized this time this program was run",
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1325,15 +1143,6 @@ impl Type for DynSimOnly {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimValueDebug for DynSimOnly {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimOnlyValueTrait> Type for SimOnly<T> {
|
impl<T: SimOnlyValueTrait> Type for SimOnly<T> {
|
||||||
type BaseType = DynSimOnly;
|
type BaseType = DynSimOnly;
|
||||||
type MaskType = Bool;
|
type MaskType = Bool;
|
||||||
|
|
@ -1399,15 +1208,6 @@ impl<T: SimOnlyValueTrait> Type for SimOnly<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SimOnlyValueTrait> SimValueDebug for SimOnly<T> {
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(value, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimOnlyValueTrait> StaticType for SimOnly<T> {
|
impl<T: SimOnlyValueTrait> StaticType for SimOnly<T> {
|
||||||
const TYPE: Self = Self::new();
|
const TYPE: Self = Self::new();
|
||||||
|
|
||||||
|
|
@ -1479,6 +1279,8 @@ impl<T: SimOnlyValueTrait> ToSimValueWithType<SimOnly<T>> for SimOnlyValue<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToSimValue for DynSimOnlyValue {
|
impl ToSimValue for DynSimOnlyValue {
|
||||||
|
type Type = DynSimOnly;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_value(self.ty(), self.clone())
|
SimValue::from_value(self.ty(), self.clone())
|
||||||
}
|
}
|
||||||
|
|
@ -1488,62 +1290,15 @@ impl ToSimValue for DynSimOnlyValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SimOnlyValueTrait> ValueType for SimOnlyValue<T> {
|
|
||||||
type Type = SimOnly<T>;
|
|
||||||
type ValueCategory = ValueCategoryValue;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
SimOnly::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimOnlyValueTrait> ToSimValue for SimOnlyValue<T> {
|
impl<T: SimOnlyValueTrait> ToSimValue for SimOnlyValue<T> {
|
||||||
|
type Type = SimOnly<T>;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_value(self.ty(), self.clone())
|
SimValue::from_value(Default::default(), self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_sim_value(self) -> SimValue<Self::Type> {
|
fn into_sim_value(self) -> SimValue<Self::Type> {
|
||||||
SimValue::from_value(self.ty(), self)
|
SimValue::from_value(Default::default(), self)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HdlPartialEqImpl<Self> for DynSimOnly {
|
|
||||||
const TRY_STRUCTURAL_EQ: bool = false;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_eq(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: Self,
|
|
||||||
rhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
*lhs_value == *rhs_value
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_eq(_lhs: Expr<Self>, _rhs: Expr<Self>) -> Expr<Bool> {
|
|
||||||
panic!("can't compare Expr<DynSimOnly>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L: SimOnlyValueTrait + PartialEq<R>, R: SimOnlyValueTrait> HdlPartialEqImpl<SimOnly<R>>
|
|
||||||
for SimOnly<L>
|
|
||||||
{
|
|
||||||
const TRY_STRUCTURAL_EQ: bool = false;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_value_eq(
|
|
||||||
_lhs: Self,
|
|
||||||
lhs_value: Cow<'_, Self::SimValue>,
|
|
||||||
_rhs: SimOnly<R>,
|
|
||||||
rhs_value: Cow<'_, <SimOnly<R> as Type>::SimValue>,
|
|
||||||
) -> bool {
|
|
||||||
**lhs_value == **rhs_value
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn cmp_expr_eq(_lhs: Expr<Self>, _rhs: Expr<SimOnly<R>>) -> Expr<Bool> {
|
|
||||||
panic!("can't compare Expr<SimOnly<_>>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
|
|
||||||
//! `unsafe` parts of [`DynSimOnlyValue`]
|
//! `unsafe` parts of [`DynSimOnlyValue`]
|
||||||
|
|
||||||
use crate::{
|
|
||||||
expr::{ValueType, value_category::ValueCategoryValue},
|
|
||||||
util::serde_by_id::SerdeByIdProperties,
|
|
||||||
};
|
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use std::{
|
use std::{
|
||||||
any::{self, TypeId},
|
any::{self, TypeId},
|
||||||
|
|
@ -36,7 +32,6 @@ unsafe trait DynSimOnlyTrait: 'static + Send + Sync {
|
||||||
&self,
|
&self,
|
||||||
json_str: &str,
|
json_str: &str,
|
||||||
) -> serde_json::Result<Rc<dyn DynSimOnlyValueTrait>>;
|
) -> serde_json::Result<Rc<dyn DynSimOnlyValueTrait>>;
|
||||||
fn serde_by_id_properties_inner(&self) -> SerdeByIdProperties<DynSimOnly>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safety: `type_id_dyn` is implemented correctly
|
/// Safety: `type_id_dyn` is implemented correctly
|
||||||
|
|
@ -59,9 +54,6 @@ unsafe impl<T: SimOnlyValueTrait> DynSimOnlyTrait for SimOnly<T> {
|
||||||
) -> serde_json::Result<Rc<dyn DynSimOnlyValueTrait>> {
|
) -> serde_json::Result<Rc<dyn DynSimOnlyValueTrait>> {
|
||||||
Ok(Rc::<T>::new(serde_json::from_str(json_str)?))
|
Ok(Rc::<T>::new(serde_json::from_str(json_str)?))
|
||||||
}
|
}
|
||||||
fn serde_by_id_properties_inner(&self) -> SerdeByIdProperties<DynSimOnly> {
|
|
||||||
SerdeByIdProperties::of::<T>()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safety:
|
/// Safety:
|
||||||
|
|
@ -158,9 +150,6 @@ impl DynSimOnly {
|
||||||
pub fn default_value(self) -> DynSimOnlyValue {
|
pub fn default_value(self) -> DynSimOnlyValue {
|
||||||
DynSimOnlyValue(self.ty.default_value())
|
DynSimOnlyValue(self.ty.default_value())
|
||||||
}
|
}
|
||||||
pub(super) fn serde_by_id_properties_inner(self) -> SerdeByIdProperties<Self> {
|
|
||||||
self.ty.serde_by_id_properties_inner()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for DynSimOnly {
|
impl PartialEq for DynSimOnly {
|
||||||
|
|
@ -217,25 +206,9 @@ impl<T: SimOnlyValueTrait> Default for SimOnly<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a value that can only be used in a Fayalite simulation, it can't be converted to FIRRTL
|
/// a value that can only be used in a Fayalite simulation, it can't be converted to FIRRTL
|
||||||
#[derive(Clone, Eq, Hash, Default, Ord)]
|
#[derive(Clone, Eq, PartialEq, Hash, Default, PartialOrd, Ord)]
|
||||||
pub struct SimOnlyValue<T: SimOnlyValueTrait>(Rc<T>);
|
pub struct SimOnlyValue<T: SimOnlyValueTrait>(Rc<T>);
|
||||||
|
|
||||||
impl<T: SimOnlyValueTrait + PartialEq<U>, U: SimOnlyValueTrait> PartialEq<SimOnlyValue<U>>
|
|
||||||
for SimOnlyValue<T>
|
|
||||||
{
|
|
||||||
fn eq(&self, other: &SimOnlyValue<U>) -> bool {
|
|
||||||
<T as PartialEq<U>>::eq(self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimOnlyValueTrait + PartialOrd<U>, U: SimOnlyValueTrait> PartialOrd<SimOnlyValue<U>>
|
|
||||||
for SimOnlyValue<T>
|
|
||||||
{
|
|
||||||
fn partial_cmp(&self, other: &SimOnlyValue<U>) -> Option<std::cmp::Ordering> {
|
|
||||||
<T as PartialOrd<U>>::partial_cmp(self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimOnlyValueTrait> SimOnlyValue<T> {
|
impl<T: SimOnlyValueTrait> SimOnlyValue<T> {
|
||||||
pub fn with_dyn_ref<F: FnOnce(&DynSimOnlyValue) -> R, R>(&self, f: F) -> R {
|
pub fn with_dyn_ref<F: FnOnce(&DynSimOnlyValue) -> R, R>(&self, f: F) -> R {
|
||||||
// Safety: creating a copied `Rc<T>` is safe as long as the copy isn't dropped and isn't changed
|
// Safety: creating a copied `Rc<T>` is safe as long as the copy isn't dropped and isn't changed
|
||||||
|
|
@ -306,16 +279,10 @@ impl<T: SimOnlyValueTrait> From<SimOnlyValue<T>> for DynSimOnlyValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueType for DynSimOnlyValue {
|
impl DynSimOnlyValue {
|
||||||
type Type = DynSimOnly;
|
pub fn ty(&self) -> DynSimOnly {
|
||||||
type ValueCategory = ValueCategoryValue;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
self.0.ty()
|
self.0.ty()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl DynSimOnlyValue {
|
|
||||||
pub fn type_id(&self) -> TypeId {
|
pub fn type_id(&self) -> TypeId {
|
||||||
self.0.type_id_dyn()
|
self.0.type_id_dyn()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,47 +6,27 @@ use crate::{
|
||||||
expr::Flow,
|
expr::Flow,
|
||||||
int::UInt,
|
int::UInt,
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
prelude::PhantomConst,
|
|
||||||
sim::{
|
sim::{
|
||||||
TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl,
|
TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl,
|
||||||
TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceFormalInput,
|
TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceInstance,
|
||||||
TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation,
|
TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceModule,
|
||||||
TraceModule, TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt, TraceScalar,
|
TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSimOnly,
|
||||||
TraceScalarId, TraceScope, TraceSimOnly, TraceSyncReset, TraceTraceAsString, TraceUInt,
|
TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls,
|
||||||
TraceWire, TraceWriter, TraceWriterDecls,
|
|
||||||
time::{SimDuration, SimInstant},
|
time::{SimDuration, SimInstant},
|
||||||
value::DynSimOnlyValue,
|
value::DynSimOnlyValue,
|
||||||
},
|
},
|
||||||
ty::{OpaqueSimValueSlice, TraceAsString},
|
|
||||||
util::HashMap,
|
util::HashMap,
|
||||||
};
|
};
|
||||||
use bitvec::{order::Lsb0, slice::BitSlice};
|
use bitvec::{order::Lsb0, slice::BitSlice};
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
|
||||||
fmt::{self, Write as _},
|
fmt::{self, Write as _},
|
||||||
io, mem,
|
io, mem,
|
||||||
num::NonZeroU64,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default)]
|
||||||
struct PathHash(Sha256);
|
|
||||||
|
|
||||||
impl PathHash {
|
|
||||||
fn joined(mut self, segment: impl AsRef<[u8]>) -> Self {
|
|
||||||
let segment = segment.as_ref();
|
|
||||||
self.0.update(u32::to_le_bytes(
|
|
||||||
segment.len().try_into().expect("path segment is too big"),
|
|
||||||
));
|
|
||||||
self.0.update(segment);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
last_inserted: HashMap<Interned<str>, usize>,
|
last_inserted: HashMap<Interned<str>, usize>,
|
||||||
path_hash: PathHash,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
|
@ -79,13 +59,6 @@ impl fmt::Display for VerilogIdentifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
fn new(path_hash: PathHash) -> Self {
|
|
||||||
Self {
|
|
||||||
last_inserted: Default::default(),
|
|
||||||
path_hash,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_identifier(&mut self, unescaped_name: Interned<str>) -> VerilogIdentifier {
|
fn new_identifier(&mut self, unescaped_name: Interned<str>) -> VerilogIdentifier {
|
||||||
let next_disambiguator = match self.last_inserted.entry(unescaped_name) {
|
let next_disambiguator = match self.last_inserted.entry(unescaped_name) {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
|
|
@ -188,26 +161,6 @@ impl<W: io::Write> fmt::Debug for VcdWriterDecls<W> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// pass in scope to ensure it's not available in child scope
|
|
||||||
fn try_write_vcd_scope<W: io::Write, R>(
|
|
||||||
writer: &mut W,
|
|
||||||
scope_type: &str,
|
|
||||||
scope_name: Interned<str>,
|
|
||||||
scope: Option<&mut Scope>,
|
|
||||||
f: impl FnOnce(&mut W, Option<&mut Scope>) -> io::Result<R>,
|
|
||||||
) -> io::Result<R> {
|
|
||||||
let Some(scope) = scope else {
|
|
||||||
return f(writer, None);
|
|
||||||
};
|
|
||||||
write_vcd_scope(
|
|
||||||
writer,
|
|
||||||
scope_type,
|
|
||||||
scope_name,
|
|
||||||
scope,
|
|
||||||
move |writer, scope| f(writer, Some(scope)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// pass in scope to ensure it's not available in child scope
|
/// pass in scope to ensure it's not available in child scope
|
||||||
fn write_vcd_scope<W: io::Write, R>(
|
fn write_vcd_scope<W: io::Write, R>(
|
||||||
writer: &mut W,
|
writer: &mut W,
|
||||||
|
|
@ -216,10 +169,12 @@ fn write_vcd_scope<W: io::Write, R>(
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
f: impl FnOnce(&mut W, &mut Scope) -> io::Result<R>,
|
f: impl FnOnce(&mut W, &mut Scope) -> io::Result<R>,
|
||||||
) -> io::Result<R> {
|
) -> io::Result<R> {
|
||||||
let path_hash = scope.path_hash.clone().joined(scope_name);
|
writeln!(
|
||||||
let scope_name = scope.new_identifier(scope_name);
|
writer,
|
||||||
writeln!(writer, "$scope {scope_type} {scope_name} $end")?;
|
"$scope {scope_type} {} $end",
|
||||||
let retval = f(writer, &mut Scope::new(path_hash))?;
|
scope.new_identifier(scope_name),
|
||||||
|
)?;
|
||||||
|
let retval = f(writer, &mut Scope::default())?;
|
||||||
writeln!(writer, "$upscope $end")?;
|
writeln!(writer, "$upscope $end")?;
|
||||||
Ok(retval)
|
Ok(retval)
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +214,6 @@ trait_arg! {
|
||||||
struct ArgModule<'a> {
|
struct ArgModule<'a> {
|
||||||
properties: &'a mut VcdWriterProperties,
|
properties: &'a mut VcdWriterProperties,
|
||||||
scope: &'a mut Scope,
|
scope: &'a mut Scope,
|
||||||
instance_name: Option<Interned<str>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ArgModule<'a> {
|
impl<'a> ArgModule<'a> {
|
||||||
|
|
@ -267,7 +221,6 @@ impl<'a> ArgModule<'a> {
|
||||||
ArgModule {
|
ArgModule {
|
||||||
properties: self.properties,
|
properties: self.properties,
|
||||||
scope: self.scope,
|
scope: self.scope,
|
||||||
instance_name: self.instance_name,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -291,7 +244,7 @@ struct ArgInType<'a> {
|
||||||
sink_var_type: &'static str,
|
sink_var_type: &'static str,
|
||||||
duplex_var_type: &'static str,
|
duplex_var_type: &'static str,
|
||||||
properties: &'a mut VcdWriterProperties,
|
properties: &'a mut VcdWriterProperties,
|
||||||
scope: Option<&'a mut Scope>,
|
scope: &'a mut Scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ArgInType<'a> {
|
impl<'a> ArgInType<'a> {
|
||||||
|
|
@ -301,7 +254,7 @@ impl<'a> ArgInType<'a> {
|
||||||
sink_var_type: self.sink_var_type,
|
sink_var_type: self.sink_var_type,
|
||||||
duplex_var_type: self.duplex_var_type,
|
duplex_var_type: self.duplex_var_type,
|
||||||
properties: self.properties,
|
properties: self.properties,
|
||||||
scope: self.scope.as_deref_mut(),
|
scope: self.scope,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -330,85 +283,24 @@ impl WriteTrace for TraceScalar {
|
||||||
Self::Clock(v) => v.write_trace(writer, arg),
|
Self::Clock(v) => v.write_trace(writer, arg),
|
||||||
Self::SyncReset(v) => v.write_trace(writer, arg),
|
Self::SyncReset(v) => v.write_trace(writer, arg),
|
||||||
Self::AsyncReset(v) => v.write_trace(writer, arg),
|
Self::AsyncReset(v) => v.write_trace(writer, arg),
|
||||||
Self::PhantomConst(v) => v.write_trace(writer, arg),
|
|
||||||
Self::SimOnly(v) => v.write_trace(writer, arg),
|
Self::SimOnly(v) => v.write_trace(writer, arg),
|
||||||
Self::TraceAsString(v) => v.write_trace(writer, arg),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
fn write_vcd_id<W: io::Write>(writer: &mut W, mut id: usize) -> io::Result<()> {
|
||||||
#[repr(transparent)]
|
let min_char = b'!';
|
||||||
struct VcdId(NonZeroU64);
|
let max_char = b'~';
|
||||||
|
let base = (max_char - min_char + 1) as usize;
|
||||||
impl VcdId {
|
|
||||||
const CHAR_RANGE: std::ops::RangeInclusive<u8> = b'!'..=b'~';
|
|
||||||
const BASE: u8 = *Self::CHAR_RANGE.end() - *Self::CHAR_RANGE.start() + 1;
|
|
||||||
const LOW_HALF_CHARS: u32 = 5;
|
|
||||||
const LOW_HALF_MODULUS: u64 = (Self::BASE as u64).pow(Self::LOW_HALF_CHARS);
|
|
||||||
|
|
||||||
const fn from_str(s: &str) -> Option<Self> {
|
|
||||||
if s.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut retval = 0u64;
|
|
||||||
let mut bytes = s.as_bytes();
|
|
||||||
while let [ref rest @ .., digit] = *bytes {
|
|
||||||
bytes = rest;
|
|
||||||
let Some(digit) = digit.checked_sub(*Self::CHAR_RANGE.start()) else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
if digit >= Self::BASE {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let Some(v) = retval.checked_mul(Self::BASE as _) else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let Some(v) = v.checked_add(digit as _) else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
retval = v;
|
|
||||||
}
|
|
||||||
let Some(retval) = NonZeroU64::new(retval) else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
Some(Self(retval))
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
const fn write(self, out: &mut [u8]) -> usize {
|
|
||||||
let mut id = self.0.get();
|
|
||||||
let mut len = 0;
|
|
||||||
loop {
|
loop {
|
||||||
let digit = (id % Self::BASE as u64) as u8 + *Self::CHAR_RANGE.start();
|
let digit = (id % base) as u8 + min_char;
|
||||||
id /= Self::BASE as u64;
|
id /= base;
|
||||||
if len < out.len() {
|
writer.write_all(&[digit])?;
|
||||||
out[len] = digit;
|
|
||||||
}
|
|
||||||
len += 1;
|
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
len
|
Ok(())
|
||||||
}
|
|
||||||
const MAX_ID_LEN: usize = Self(NonZeroU64::MAX).write(&mut []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// check that VcdId properly round-trips
|
|
||||||
const _: () = {
|
|
||||||
let s = "RoundTrip";
|
|
||||||
let Some(id) = VcdId::from_str(s) else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
let mut buf = [0u8; VcdId::MAX_ID_LEN];
|
|
||||||
let len = id.write(&mut buf);
|
|
||||||
assert!(crate::util::const_bytes_cmp(buf.split_at(len).0, s.as_bytes()).is_eq());
|
|
||||||
};
|
|
||||||
|
|
||||||
fn write_vcd_id<W: io::Write>(writer: &mut W, id: VcdId) -> io::Result<()> {
|
|
||||||
let mut buf = [0u8; VcdId::MAX_ID_LEN];
|
|
||||||
let len = id.write(&mut buf);
|
|
||||||
writer.write_all(&buf[..len])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Escaped<T: fmt::Display>(T);
|
struct Escaped<T: fmt::Display>(T);
|
||||||
|
|
@ -451,13 +343,12 @@ impl<T: fmt::Display> fmt::Display for Escaped<T> {
|
||||||
|
|
||||||
fn write_vcd_var<W: io::Write>(
|
fn write_vcd_var<W: io::Write>(
|
||||||
properties: &mut VcdWriterProperties,
|
properties: &mut VcdWriterProperties,
|
||||||
scope: Option<&mut Scope>,
|
|
||||||
memory_element_part_body: MemoryElementPartBody,
|
memory_element_part_body: MemoryElementPartBody,
|
||||||
writer: &mut W,
|
writer: &mut W,
|
||||||
var_type: &str,
|
var_type: &str,
|
||||||
size: usize,
|
size: usize,
|
||||||
location: TraceLocation,
|
location: TraceLocation,
|
||||||
name: Interned<str>,
|
name: VerilogIdentifier,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let id = match location {
|
let id = match location {
|
||||||
TraceLocation::Scalar(id) => id.as_usize(),
|
TraceLocation::Scalar(id) => id.as_usize(),
|
||||||
|
|
@ -490,21 +381,9 @@ fn write_vcd_var<W: io::Write>(
|
||||||
first_id + *element_index
|
first_id + *element_index
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(scope) = scope {
|
|
||||||
let path_hash = scope.path_hash.clone().joined(name);
|
|
||||||
let name = scope.new_identifier(name);
|
|
||||||
let id = properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.builder_get_or_insert(id, &path_hash);
|
|
||||||
write!(writer, "$var {var_type} {size} ")?;
|
write!(writer, "$var {var_type} {size} ")?;
|
||||||
write_vcd_id(writer, id)?;
|
write_vcd_id(writer, id)?;
|
||||||
writeln!(writer, " {name} $end")
|
writeln!(writer, " {name} $end")
|
||||||
} else {
|
|
||||||
properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.builder_unused_scalar_id(id);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteTrace for TraceUInt {
|
impl WriteTrace for TraceUInt {
|
||||||
|
|
@ -532,13 +411,12 @@ impl WriteTrace for TraceUInt {
|
||||||
}
|
}
|
||||||
write_vcd_var(
|
write_vcd_var(
|
||||||
properties,
|
properties,
|
||||||
scope,
|
|
||||||
MemoryElementPartBody::Scalar,
|
MemoryElementPartBody::Scalar,
|
||||||
writer,
|
writer,
|
||||||
var_type,
|
var_type,
|
||||||
ty.width(),
|
ty.width(),
|
||||||
location,
|
location,
|
||||||
name,
|
scope.new_identifier(name),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -613,13 +491,12 @@ impl WriteTrace for TraceEnumDiscriminant {
|
||||||
} = self;
|
} = self;
|
||||||
write_vcd_var(
|
write_vcd_var(
|
||||||
properties,
|
properties,
|
||||||
scope,
|
|
||||||
MemoryElementPartBody::EnumDiscriminant { ty },
|
MemoryElementPartBody::EnumDiscriminant { ty },
|
||||||
writer,
|
writer,
|
||||||
"string",
|
"string",
|
||||||
1,
|
1,
|
||||||
location,
|
location,
|
||||||
name,
|
scope.new_identifier(name),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -672,34 +549,6 @@ impl WriteTrace for TraceAsyncReset {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteTrace for TracePhantomConst {
|
|
||||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
|
||||||
let ArgInType {
|
|
||||||
source_var_type: _,
|
|
||||||
sink_var_type: _,
|
|
||||||
duplex_var_type: _,
|
|
||||||
properties,
|
|
||||||
scope,
|
|
||||||
} = arg.in_type();
|
|
||||||
let Self {
|
|
||||||
location,
|
|
||||||
name,
|
|
||||||
ty: _,
|
|
||||||
flow: _,
|
|
||||||
} = self;
|
|
||||||
write_vcd_var(
|
|
||||||
properties,
|
|
||||||
scope,
|
|
||||||
MemoryElementPartBody::Scalar,
|
|
||||||
writer,
|
|
||||||
"string",
|
|
||||||
1,
|
|
||||||
location,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WriteTrace for TraceSimOnly {
|
impl WriteTrace for TraceSimOnly {
|
||||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||||
let ArgInType {
|
let ArgInType {
|
||||||
|
|
@ -717,41 +566,12 @@ impl WriteTrace for TraceSimOnly {
|
||||||
} = self;
|
} = self;
|
||||||
write_vcd_var(
|
write_vcd_var(
|
||||||
properties,
|
properties,
|
||||||
scope,
|
|
||||||
MemoryElementPartBody::Scalar,
|
MemoryElementPartBody::Scalar,
|
||||||
writer,
|
writer,
|
||||||
"string",
|
"string",
|
||||||
1,
|
1,
|
||||||
location,
|
location,
|
||||||
name,
|
scope.new_identifier(name),
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WriteTrace for TraceTraceAsString {
|
|
||||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
|
||||||
let ArgInType {
|
|
||||||
source_var_type: _,
|
|
||||||
sink_var_type: _,
|
|
||||||
duplex_var_type: _,
|
|
||||||
properties,
|
|
||||||
scope,
|
|
||||||
} = arg.in_type();
|
|
||||||
let Self {
|
|
||||||
location,
|
|
||||||
name,
|
|
||||||
ty,
|
|
||||||
flow: _,
|
|
||||||
} = self;
|
|
||||||
write_vcd_var(
|
|
||||||
properties,
|
|
||||||
scope,
|
|
||||||
MemoryElementPartBody::TraceAsString { ty },
|
|
||||||
writer,
|
|
||||||
"string",
|
|
||||||
1,
|
|
||||||
location,
|
|
||||||
name,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -766,7 +586,6 @@ impl WriteTrace for TraceScope {
|
||||||
Self::Wire(v) => v.write_trace(writer, arg),
|
Self::Wire(v) => v.write_trace(writer, arg),
|
||||||
Self::Reg(v) => v.write_trace(writer, arg),
|
Self::Reg(v) => v.write_trace(writer, arg),
|
||||||
Self::ModuleIO(v) => v.write_trace(writer, arg),
|
Self::ModuleIO(v) => v.write_trace(writer, arg),
|
||||||
Self::FormalInput(v) => v.write_trace(writer, arg),
|
|
||||||
Self::Bundle(v) => v.write_trace(writer, arg),
|
Self::Bundle(v) => v.write_trace(writer, arg),
|
||||||
Self::Array(v) => v.write_trace(writer, arg),
|
Self::Array(v) => v.write_trace(writer, arg),
|
||||||
Self::EnumWithFields(v) => v.write_trace(writer, arg),
|
Self::EnumWithFields(v) => v.write_trace(writer, arg),
|
||||||
|
|
@ -776,24 +595,14 @@ impl WriteTrace for TraceScope {
|
||||||
|
|
||||||
impl WriteTrace for TraceModule {
|
impl WriteTrace for TraceModule {
|
||||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||||
let ArgModule {
|
let ArgModule { properties, scope } = arg.module();
|
||||||
properties,
|
|
||||||
scope,
|
|
||||||
instance_name,
|
|
||||||
} = arg.module();
|
|
||||||
let Self { name, children } = self;
|
let Self { name, children } = self;
|
||||||
write_vcd_scope(
|
write_vcd_scope(writer, "module", name, scope, |writer, scope| {
|
||||||
writer,
|
|
||||||
"module",
|
|
||||||
instance_name.unwrap_or(name),
|
|
||||||
scope,
|
|
||||||
|writer, scope| {
|
|
||||||
for child in children {
|
for child in children {
|
||||||
child.write_trace(writer, ArgModuleBody { properties, scope })?;
|
child.write_trace(writer, ArgModuleBody { properties, scope })?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -801,7 +610,7 @@ impl WriteTrace for TraceInstance {
|
||||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||||
let ArgModuleBody { properties, scope } = arg.module_body();
|
let ArgModuleBody { properties, scope } = arg.module_body();
|
||||||
let Self {
|
let Self {
|
||||||
name,
|
name: _,
|
||||||
instance_io,
|
instance_io,
|
||||||
module,
|
module,
|
||||||
ty: _,
|
ty: _,
|
||||||
|
|
@ -813,17 +622,10 @@ impl WriteTrace for TraceInstance {
|
||||||
sink_var_type: "wire",
|
sink_var_type: "wire",
|
||||||
duplex_var_type: "wire",
|
duplex_var_type: "wire",
|
||||||
properties,
|
properties,
|
||||||
scope: None,
|
scope,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
module.write_trace(
|
module.write_trace(writer, ArgModule { properties, scope })
|
||||||
writer,
|
|
||||||
ArgModule {
|
|
||||||
properties,
|
|
||||||
scope,
|
|
||||||
instance_name: Some(name),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -862,7 +664,7 @@ impl WriteTrace for TraceMem {
|
||||||
sink_var_type: "reg",
|
sink_var_type: "reg",
|
||||||
duplex_var_type: "reg",
|
duplex_var_type: "reg",
|
||||||
properties,
|
properties,
|
||||||
scope: Some(scope),
|
scope,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -894,7 +696,7 @@ impl WriteTrace for TraceMemPort {
|
||||||
sink_var_type: "wire",
|
sink_var_type: "wire",
|
||||||
duplex_var_type: "wire",
|
duplex_var_type: "wire",
|
||||||
properties,
|
properties,
|
||||||
scope: Some(scope),
|
scope,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -915,7 +717,7 @@ impl WriteTrace for TraceWire {
|
||||||
sink_var_type: "wire",
|
sink_var_type: "wire",
|
||||||
duplex_var_type: "wire",
|
duplex_var_type: "wire",
|
||||||
properties,
|
properties,
|
||||||
scope: Some(scope),
|
scope,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -936,7 +738,7 @@ impl WriteTrace for TraceReg {
|
||||||
sink_var_type: "reg",
|
sink_var_type: "reg",
|
||||||
duplex_var_type: "reg",
|
duplex_var_type: "reg",
|
||||||
properties,
|
properties,
|
||||||
scope: Some(scope),
|
scope,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -958,28 +760,7 @@ impl WriteTrace for TraceModuleIO {
|
||||||
sink_var_type: "wire",
|
sink_var_type: "wire",
|
||||||
duplex_var_type: "wire",
|
duplex_var_type: "wire",
|
||||||
properties,
|
properties,
|
||||||
scope: Some(scope),
|
scope,
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WriteTrace for TraceFormalInput {
|
|
||||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
|
||||||
let ArgModuleBody { properties, scope } = arg.module_body();
|
|
||||||
let Self {
|
|
||||||
name: _,
|
|
||||||
child,
|
|
||||||
formal_input: _,
|
|
||||||
} = self;
|
|
||||||
child.write_trace(
|
|
||||||
writer,
|
|
||||||
ArgInType {
|
|
||||||
source_var_type: "wire",
|
|
||||||
sink_var_type: "wire",
|
|
||||||
duplex_var_type: "wire",
|
|
||||||
properties,
|
|
||||||
scope: Some(scope),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1000,7 +781,7 @@ impl WriteTrace for TraceBundle {
|
||||||
ty: _,
|
ty: _,
|
||||||
flow: _,
|
flow: _,
|
||||||
} = self;
|
} = self;
|
||||||
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| {
|
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
|
||||||
for field in fields {
|
for field in fields {
|
||||||
field.write_trace(
|
field.write_trace(
|
||||||
writer,
|
writer,
|
||||||
|
|
@ -1009,7 +790,7 @@ impl WriteTrace for TraceBundle {
|
||||||
sink_var_type,
|
sink_var_type,
|
||||||
duplex_var_type,
|
duplex_var_type,
|
||||||
properties,
|
properties,
|
||||||
scope: scope.as_deref_mut(),
|
scope,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
@ -1033,7 +814,7 @@ impl WriteTrace for TraceArray {
|
||||||
ty: _,
|
ty: _,
|
||||||
flow: _,
|
flow: _,
|
||||||
} = self;
|
} = self;
|
||||||
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| {
|
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
|
||||||
for element in elements {
|
for element in elements {
|
||||||
element.write_trace(
|
element.write_trace(
|
||||||
writer,
|
writer,
|
||||||
|
|
@ -1042,7 +823,7 @@ impl WriteTrace for TraceArray {
|
||||||
sink_var_type,
|
sink_var_type,
|
||||||
duplex_var_type,
|
duplex_var_type,
|
||||||
properties,
|
properties,
|
||||||
scope: scope.as_deref_mut(),
|
scope,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
@ -1067,7 +848,7 @@ impl WriteTrace for TraceEnumWithFields {
|
||||||
ty: _,
|
ty: _,
|
||||||
flow: _,
|
flow: _,
|
||||||
} = self;
|
} = self;
|
||||||
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| {
|
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
|
||||||
discriminant.write_trace(
|
discriminant.write_trace(
|
||||||
writer,
|
writer,
|
||||||
ArgInType {
|
ArgInType {
|
||||||
|
|
@ -1075,7 +856,7 @@ impl WriteTrace for TraceEnumWithFields {
|
||||||
sink_var_type,
|
sink_var_type,
|
||||||
duplex_var_type,
|
duplex_var_type,
|
||||||
properties,
|
properties,
|
||||||
scope: scope.as_deref_mut(),
|
scope,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
for field in non_empty_fields {
|
for field in non_empty_fields {
|
||||||
|
|
@ -1086,7 +867,7 @@ impl WriteTrace for TraceEnumWithFields {
|
||||||
sink_var_type,
|
sink_var_type,
|
||||||
duplex_var_type,
|
duplex_var_type,
|
||||||
properties,
|
properties,
|
||||||
scope: scope.as_deref_mut(),
|
scope,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
@ -1112,9 +893,6 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
|
||||||
writeln!(writer, "$timescale {} $end", vcd_timescale(timescale))?;
|
writeln!(writer, "$timescale {} $end", vcd_timescale(timescale))?;
|
||||||
let mut properties = VcdWriterProperties {
|
let mut properties = VcdWriterProperties {
|
||||||
next_scalar_id: trace_scalar_id_count,
|
next_scalar_id: trace_scalar_id_count,
|
||||||
scalar_id_to_vcd_id_map: ScalarIdToVcdIdMapOrBuilder::Builder(
|
|
||||||
ScalarIdToVcdIdMapBuilder::default(),
|
|
||||||
),
|
|
||||||
memory_properties: (0..trace_memory_id_count)
|
memory_properties: (0..trace_memory_id_count)
|
||||||
.map(|_| MemoryProperties {
|
.map(|_| MemoryProperties {
|
||||||
element_parts: Vec::with_capacity(8),
|
element_parts: Vec::with_capacity(8),
|
||||||
|
|
@ -1127,17 +905,9 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
|
||||||
&mut writer,
|
&mut writer,
|
||||||
ArgModule {
|
ArgModule {
|
||||||
properties: &mut properties,
|
properties: &mut properties,
|
||||||
scope: &mut Scope::new(PathHash::default()),
|
scope: &mut Scope::default(),
|
||||||
instance_name: None,
|
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
let ScalarIdToVcdIdMapOrBuilder::Builder(scalar_id_to_vcd_id_map_builder) =
|
|
||||||
properties.scalar_id_to_vcd_id_map
|
|
||||||
else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
properties.scalar_id_to_vcd_id_map =
|
|
||||||
ScalarIdToVcdIdMapOrBuilder::Built(scalar_id_to_vcd_id_map_builder.build());
|
|
||||||
writeln!(writer, "$enddefinitions $end")?;
|
writeln!(writer, "$enddefinitions $end")?;
|
||||||
writeln!(writer, "$dumpvars")?;
|
writeln!(writer, "$dumpvars")?;
|
||||||
Ok(VcdWriter {
|
Ok(VcdWriter {
|
||||||
|
|
@ -1145,7 +915,6 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
|
||||||
finished_init: false,
|
finished_init: false,
|
||||||
timescale,
|
timescale,
|
||||||
properties,
|
properties,
|
||||||
trace_as_string_buf: String::with_capacity(256),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1153,7 +922,6 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
|
||||||
enum MemoryElementPartBody {
|
enum MemoryElementPartBody {
|
||||||
Scalar,
|
Scalar,
|
||||||
EnumDiscriminant { ty: Enum },
|
EnumDiscriminant { ty: Enum },
|
||||||
TraceAsString { ty: TraceAsString },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MemoryElementPart {
|
struct MemoryElementPart {
|
||||||
|
|
@ -1169,100 +937,8 @@ struct MemoryProperties {
|
||||||
element_index: usize,
|
element_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScalarIdToVcdIdMap {
|
|
||||||
scalar_id_to_vcd_id_map: Box<[Option<VcdId>]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ScalarIdToVcdIdMapBuilder {
|
|
||||||
scalar_id_to_vcd_id_map: BTreeMap<usize, Option<VcdId>>,
|
|
||||||
lower_half_to_next_upper_half_map: HashMap<u64, u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScalarIdToVcdIdMapBuilder {
|
|
||||||
fn unused_scalar_id(&mut self, scalar_id: usize) {
|
|
||||||
self.scalar_id_to_vcd_id_map
|
|
||||||
.entry(scalar_id)
|
|
||||||
.or_insert(None);
|
|
||||||
}
|
|
||||||
/// `VcdId`s are based off of `path_hash` (and not `scalar_id`) since the hash doesn't change
|
|
||||||
/// when unrelated variables are added/removed, making the generated VCD more friendly for git diff.
|
|
||||||
fn get_or_insert(&mut self, scalar_id: usize, path_hash: &PathHash) -> VcdId {
|
|
||||||
*self
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.entry(scalar_id)
|
|
||||||
.or_insert(None)
|
|
||||||
.get_or_insert_with(|| {
|
|
||||||
let hash = u128::from_le_bytes(
|
|
||||||
*path_hash
|
|
||||||
.0
|
|
||||||
.clone()
|
|
||||||
.finalize()
|
|
||||||
.first_chunk()
|
|
||||||
.expect("known to be bigger than u128"),
|
|
||||||
);
|
|
||||||
let lower_half = (hash % VcdId::LOW_HALF_MODULUS as u128) as u64;
|
|
||||||
let next_upper_half = self
|
|
||||||
.lower_half_to_next_upper_half_map
|
|
||||||
.entry(lower_half)
|
|
||||||
.or_insert(if lower_half == 0 { 1 } else { 0 });
|
|
||||||
let upper_half = *next_upper_half;
|
|
||||||
*next_upper_half += 1;
|
|
||||||
let Some(id) = upper_half
|
|
||||||
.checked_mul(VcdId::LOW_HALF_MODULUS)
|
|
||||||
.and_then(|v| v.checked_add(lower_half))
|
|
||||||
else {
|
|
||||||
panic!("too many VcdIds");
|
|
||||||
};
|
|
||||||
VcdId(NonZeroU64::new(id).expect("known to not be zero"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn build(self) -> ScalarIdToVcdIdMap {
|
|
||||||
ScalarIdToVcdIdMap {
|
|
||||||
scalar_id_to_vcd_id_map: self
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, (scalar_id, vcd_id))| {
|
|
||||||
if index != scalar_id {
|
|
||||||
panic!("missing scalar id {index}");
|
|
||||||
}
|
|
||||||
vcd_id
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ScalarIdToVcdIdMapOrBuilder {
|
|
||||||
Builder(ScalarIdToVcdIdMapBuilder),
|
|
||||||
Built(ScalarIdToVcdIdMap),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScalarIdToVcdIdMapOrBuilder {
|
|
||||||
fn built_scalar_id_to_vcd_id(&self, scalar_id: usize) -> Option<VcdId> {
|
|
||||||
let Self::Built(v) = self else {
|
|
||||||
panic!("ScalarIdToVcdIdMap isn't built yet");
|
|
||||||
};
|
|
||||||
v.scalar_id_to_vcd_id_map[scalar_id]
|
|
||||||
}
|
|
||||||
fn builder_get_or_insert(&mut self, scalar_id: usize, path_hash: &PathHash) -> VcdId {
|
|
||||||
let Self::Builder(v) = self else {
|
|
||||||
panic!("ScalarIdToVcdIdMap is already built");
|
|
||||||
};
|
|
||||||
v.get_or_insert(scalar_id, path_hash)
|
|
||||||
}
|
|
||||||
fn builder_unused_scalar_id(&mut self, scalar_id: usize) {
|
|
||||||
let Self::Builder(v) = self else {
|
|
||||||
panic!("ScalarIdToVcdIdMap is already built");
|
|
||||||
};
|
|
||||||
v.unused_scalar_id(scalar_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VcdWriterProperties {
|
struct VcdWriterProperties {
|
||||||
next_scalar_id: usize,
|
next_scalar_id: usize,
|
||||||
scalar_id_to_vcd_id_map: ScalarIdToVcdIdMapOrBuilder,
|
|
||||||
memory_properties: Box<[MemoryProperties]>,
|
memory_properties: Box<[MemoryProperties]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1271,7 +947,6 @@ pub struct VcdWriter<W: io::Write + 'static> {
|
||||||
finished_init: bool,
|
finished_init: bool,
|
||||||
timescale: SimDuration,
|
timescale: SimDuration,
|
||||||
properties: VcdWriterProperties,
|
properties: VcdWriterProperties,
|
||||||
trace_as_string_buf: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: io::Write + 'static> VcdWriter<W> {
|
impl<W: io::Write + 'static> VcdWriter<W> {
|
||||||
|
|
@ -1283,11 +958,8 @@ impl<W: io::Write + 'static> VcdWriter<W> {
|
||||||
fn write_string_value_change(
|
fn write_string_value_change(
|
||||||
writer: &mut impl io::Write,
|
writer: &mut impl io::Write,
|
||||||
value: impl fmt::Display,
|
value: impl fmt::Display,
|
||||||
id: Option<VcdId>,
|
id: usize,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let Some(id) = id else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
write!(writer, "s{} ", Escaped(value))?;
|
write!(writer, "s{} ", Escaped(value))?;
|
||||||
write_vcd_id(writer, id)?;
|
write_vcd_id(writer, id)?;
|
||||||
writer.write_all(b"\n")
|
writer.write_all(b"\n")
|
||||||
|
|
@ -1296,11 +968,8 @@ fn write_string_value_change(
|
||||||
fn write_bits_value_change(
|
fn write_bits_value_change(
|
||||||
writer: &mut impl io::Write,
|
writer: &mut impl io::Write,
|
||||||
value: &BitSlice,
|
value: &BitSlice,
|
||||||
id: Option<VcdId>,
|
id: usize,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let Some(id) = id else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
match value.len() {
|
match value.len() {
|
||||||
0 => writer.write_all(b"s0 ")?,
|
0 => writer.write_all(b"s0 ")?,
|
||||||
1 => writer.write_all(if value[0] { b"1" } else { b"0" })?,
|
1 => writer.write_all(if value[0] { b"1" } else { b"0" })?,
|
||||||
|
|
@ -1329,7 +998,7 @@ fn write_enum_discriminant_value_change(
|
||||||
writer: &mut impl io::Write,
|
writer: &mut impl io::Write,
|
||||||
variant_index: usize,
|
variant_index: usize,
|
||||||
ty: Enum,
|
ty: Enum,
|
||||||
id: Option<VcdId>,
|
id: usize,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
write_string_value_change(
|
write_string_value_change(
|
||||||
writer,
|
writer,
|
||||||
|
|
@ -1364,9 +1033,7 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
|
||||||
MemoryElementPartBody::Scalar => write_bits_value_change(
|
MemoryElementPartBody::Scalar => write_bits_value_change(
|
||||||
&mut self.writer,
|
&mut self.writer,
|
||||||
&element_data[start..start + len],
|
&element_data[start..start + len],
|
||||||
self.properties
|
first_id + element_index,
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(first_id + element_index),
|
|
||||||
)?,
|
)?,
|
||||||
MemoryElementPartBody::EnumDiscriminant { ty } => {
|
MemoryElementPartBody::EnumDiscriminant { ty } => {
|
||||||
let mut variant_index = 0;
|
let mut variant_index = 0;
|
||||||
|
|
@ -1376,49 +1043,20 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
|
||||||
&mut self.writer,
|
&mut self.writer,
|
||||||
variant_index,
|
variant_index,
|
||||||
*ty,
|
*ty,
|
||||||
self.properties
|
first_id + element_index,
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(first_id + element_index),
|
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
MemoryElementPartBody::TraceAsString { ty } => {
|
|
||||||
self.trace_as_string_buf.clear();
|
|
||||||
ty.trace_fmt_append_to_string(
|
|
||||||
&mut self.trace_as_string_buf,
|
|
||||||
OpaqueSimValueSlice::from_bitslice(&element_data[start..start + len]),
|
|
||||||
);
|
|
||||||
write_string_value_change(
|
|
||||||
&mut self.writer,
|
|
||||||
&self.trace_as_string_buf,
|
|
||||||
self.properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(first_id + element_index),
|
|
||||||
)?;
|
|
||||||
self.trace_as_string_buf.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
|
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
|
||||||
write_bits_value_change(
|
write_bits_value_change(&mut self.writer, value, id.as_usize())
|
||||||
&mut self.writer,
|
|
||||||
value,
|
|
||||||
self.properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(id.as_usize()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
|
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
|
||||||
write_bits_value_change(
|
write_bits_value_change(&mut self.writer, value, id.as_usize())
|
||||||
&mut self.writer,
|
|
||||||
value,
|
|
||||||
self.properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(id.as_usize()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_init(&mut self) -> Result<(), Self::Error> {
|
fn finish_init(&mut self) -> Result<(), Self::Error> {
|
||||||
|
|
@ -1450,30 +1088,7 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
|
||||||
variant_index: usize,
|
variant_index: usize,
|
||||||
ty: Enum,
|
ty: Enum,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
write_enum_discriminant_value_change(
|
write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize())
|
||||||
&mut self.writer,
|
|
||||||
variant_index,
|
|
||||||
ty,
|
|
||||||
self.properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(id.as_usize()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_signal_phantom_const(
|
|
||||||
&mut self,
|
|
||||||
id: TraceScalarId,
|
|
||||||
ty: PhantomConst,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
// avoid multi-line strings because GTKWave can't display them properly:
|
|
||||||
// https://github.com/gtkwave/gtkwave/issues/460
|
|
||||||
write_string_value_change(
|
|
||||||
&mut self.writer,
|
|
||||||
format_args!("{ty:?}"),
|
|
||||||
self.properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(id.as_usize()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_signal_sim_only_value(
|
fn set_signal_sim_only_value(
|
||||||
|
|
@ -1481,23 +1096,7 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
|
||||||
id: TraceScalarId,
|
id: TraceScalarId,
|
||||||
value: &DynSimOnlyValue,
|
value: &DynSimOnlyValue,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
write_string_value_change(
|
write_string_value_change(&mut self.writer, format_args!("{value:?}"), id.as_usize())
|
||||||
&mut self.writer,
|
|
||||||
format_args!("{value:?}"),
|
|
||||||
self.properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(id.as_usize()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_signal_string(&mut self, id: TraceScalarId, value: &str) -> Result<(), Self::Error> {
|
|
||||||
write_string_value_change(
|
|
||||||
&mut self.writer,
|
|
||||||
value,
|
|
||||||
self.properties
|
|
||||||
.scalar_id_to_vcd_id_map
|
|
||||||
.built_scalar_id_to_vcd_id(id.as_usize()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1508,7 +1107,6 @@ impl<W: io::Write> fmt::Debug for VcdWriter<W> {
|
||||||
finished_init,
|
finished_init,
|
||||||
timescale,
|
timescale,
|
||||||
properties: _,
|
properties: _,
|
||||||
trace_as_string_buf: _,
|
|
||||||
} = self;
|
} = self;
|
||||||
f.debug_struct("VcdWriter")
|
f.debug_struct("VcdWriter")
|
||||||
.field("finished_init", finished_init)
|
.field("finished_init", finished_init)
|
||||||
|
|
@ -1523,7 +1121,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope() {
|
fn test_scope() {
|
||||||
let mut scope = Scope::new(PathHash::default());
|
let mut scope = Scope::default();
|
||||||
assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo");
|
assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&*scope.new_identifier("foo_0".intern()).unescaped_name,
|
&*scope.new_identifier("foo_0".intern()).unescaped_name,
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,25 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
use crate::{
|
use crate::{
|
||||||
build::{
|
cli::{FormalArgs, FormalMode, FormalOutput, RunPhase},
|
||||||
BaseJobArgs, BaseJobKind, GlobalParams, JobArgsAndDependencies, JobKindAndArgs, JobParams,
|
|
||||||
NoArgs, RunBuild,
|
|
||||||
external::{ExternalCommandArgs, ExternalCommandJobKind},
|
|
||||||
firrtl::{FirrtlArgs, FirrtlJobKind},
|
|
||||||
formal::{Formal, FormalAdditionalArgs, FormalArgs, WriteSbyFileJobKind},
|
|
||||||
verilog::{UnadjustedVerilogArgs, VerilogJobArgs, VerilogJobKind},
|
|
||||||
},
|
|
||||||
bundle::BundleType,
|
|
||||||
firrtl::ExportOptions,
|
firrtl::ExportOptions,
|
||||||
module::Module,
|
util::HashMap,
|
||||||
sim::{Simulation, vcd::VcdWriterDecls},
|
|
||||||
util::{HashMap, RcWriter},
|
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use clap::Parser;
|
||||||
|
use serde::Deserialize;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Write},
|
fmt::Write,
|
||||||
panic::Location,
|
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::Command,
|
process::Command,
|
||||||
sync::{Mutex, OnceLock},
|
sync::{Mutex, OnceLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(
|
fn assert_formal_helper() -> FormalArgs {
|
||||||
clap::ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default, Deserialize, Serialize,
|
static FORMAL_ARGS: OnceLock<FormalArgs> = OnceLock::new();
|
||||||
)]
|
// ensure we only run parsing once, so errors from env vars don't produce overlapping output if we're called on multiple threads
|
||||||
#[non_exhaustive]
|
FORMAL_ARGS
|
||||||
pub enum FormalMode {
|
.get_or_init(|| FormalArgs::parse_from(["fayalite::testing::assert_formal"]))
|
||||||
#[default]
|
.clone()
|
||||||
BMC,
|
|
||||||
Prove,
|
|
||||||
Live,
|
|
||||||
Cover,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormalMode {
|
|
||||||
pub fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
FormalMode::BMC => "bmc",
|
|
||||||
FormalMode::Prove => "prove",
|
|
||||||
FormalMode::Live => "live",
|
|
||||||
FormalMode::Cover => "cover",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for FormalMode {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|
@ -128,286 +97,26 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf {
|
||||||
.join(dir)
|
.join(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_assert_formal_args(
|
|
||||||
test_name: &dyn std::fmt::Display,
|
|
||||||
formal_mode: FormalMode,
|
|
||||||
formal_depth: u64,
|
|
||||||
solver: Option<&str>,
|
|
||||||
export_options: ExportOptions,
|
|
||||||
) -> eyre::Result<JobArgsAndDependencies<ExternalCommandJobKind<Formal>>> {
|
|
||||||
let args = JobKindAndArgs {
|
|
||||||
kind: BaseJobKind,
|
|
||||||
args: BaseJobArgs::from_output_dir_and_env(get_assert_formal_target_path(&test_name), None),
|
|
||||||
};
|
|
||||||
let dependencies = JobArgsAndDependencies {
|
|
||||||
args,
|
|
||||||
dependencies: (),
|
|
||||||
};
|
|
||||||
let args = JobKindAndArgs {
|
|
||||||
kind: FirrtlJobKind,
|
|
||||||
args: FirrtlArgs { export_options },
|
|
||||||
};
|
|
||||||
let dependencies = JobArgsAndDependencies { args, dependencies };
|
|
||||||
let args = JobKindAndArgs {
|
|
||||||
kind: ExternalCommandJobKind::new(),
|
|
||||||
args: ExternalCommandArgs::resolve_program_path(
|
|
||||||
None,
|
|
||||||
UnadjustedVerilogArgs {
|
|
||||||
firtool_extra_args: vec![],
|
|
||||||
verilog_dialect: None,
|
|
||||||
verilog_debug: true,
|
|
||||||
},
|
|
||||||
)?,
|
|
||||||
};
|
|
||||||
let dependencies = JobArgsAndDependencies { args, dependencies };
|
|
||||||
let args = JobKindAndArgs {
|
|
||||||
kind: VerilogJobKind,
|
|
||||||
args: VerilogJobArgs {},
|
|
||||||
};
|
|
||||||
let dependencies = JobArgsAndDependencies { args, dependencies };
|
|
||||||
let args = JobKindAndArgs {
|
|
||||||
kind: WriteSbyFileJobKind,
|
|
||||||
args: FormalArgs {
|
|
||||||
sby_extra_args: vec![],
|
|
||||||
formal_mode,
|
|
||||||
formal_depth,
|
|
||||||
formal_solver: solver.unwrap_or(FormalArgs::DEFAULT_SOLVER).into(),
|
|
||||||
smtbmc_extra_args: vec![],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let dependencies = JobArgsAndDependencies { args, dependencies };
|
|
||||||
let args = JobKindAndArgs {
|
|
||||||
kind: ExternalCommandJobKind::new(),
|
|
||||||
args: ExternalCommandArgs::resolve_program_path(None, FormalAdditionalArgs {})?,
|
|
||||||
};
|
|
||||||
Ok(JobArgsAndDependencies { args, dependencies })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_assert_formal<M: AsRef<Module<T>>, T: BundleType>(
|
|
||||||
test_name: impl std::fmt::Display,
|
|
||||||
module: M,
|
|
||||||
formal_mode: FormalMode,
|
|
||||||
formal_depth: u64,
|
|
||||||
solver: Option<&str>,
|
|
||||||
export_options: ExportOptions,
|
|
||||||
) -> eyre::Result<()> {
|
|
||||||
const APP_NAME: &'static str = "fayalite::testing::assert_formal";
|
|
||||||
make_assert_formal_args(
|
|
||||||
&test_name,
|
|
||||||
formal_mode,
|
|
||||||
formal_depth,
|
|
||||||
solver,
|
|
||||||
export_options,
|
|
||||||
)?
|
|
||||||
.run_without_platform(
|
|
||||||
|NoArgs {}| Ok(JobParams::new(module)),
|
|
||||||
&GlobalParams::new(None, APP_NAME),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn assert_formal<M: AsRef<Module<T>>, T: BundleType>(
|
pub fn assert_formal<M>(
|
||||||
test_name: impl std::fmt::Display,
|
test_name: impl std::fmt::Display,
|
||||||
module: M,
|
module: M,
|
||||||
formal_mode: FormalMode,
|
mode: FormalMode,
|
||||||
formal_depth: u64,
|
depth: u64,
|
||||||
solver: Option<&str>,
|
solver: Option<&str>,
|
||||||
export_options: ExportOptions,
|
export_options: ExportOptions,
|
||||||
) {
|
) where
|
||||||
try_assert_formal(
|
FormalArgs: RunPhase<M, Output = FormalOutput>,
|
||||||
test_name,
|
{
|
||||||
module,
|
let mut args = assert_formal_helper();
|
||||||
formal_mode,
|
args.verilog.firrtl.base.redirect_output_for_rust_test = true;
|
||||||
formal_depth,
|
args.verilog.firrtl.base.output = Some(get_assert_formal_target_path(&test_name));
|
||||||
solver,
|
args.verilog.firrtl.export_options = export_options;
|
||||||
export_options,
|
args.verilog.debug = true;
|
||||||
)
|
args.mode = mode;
|
||||||
.expect("testing::assert_formal() failed");
|
args.depth = depth;
|
||||||
|
if let Some(solver) = solver {
|
||||||
|
args.solver = solver.into();
|
||||||
|
}
|
||||||
|
args.run(module).expect("testing::assert_formal() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CheckedVcdOutput {
|
|
||||||
writer: Option<RcWriter>,
|
|
||||||
expected_path: PathBuf,
|
|
||||||
expected_contents: Result<String, (Option<PathBuf>, std::io::Error)>,
|
|
||||||
location: &'static Location<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CheckedVcdOutput {
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
pub fn new<T: BundleType>(sim: &mut Simulation<T>, expected_path: PathBuf) -> Self {
|
|
||||||
let writer = RcWriter::default();
|
|
||||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
|
||||||
Self {
|
|
||||||
writer: Some(writer),
|
|
||||||
expected_contents: std::fs::read_to_string(&expected_path).map_err(|e| {
|
|
||||||
eprintln!(
|
|
||||||
"error: failed to read expected VCD from: {}",
|
|
||||||
expected_path.display(),
|
|
||||||
);
|
|
||||||
(std::env::current_dir().ok(), e)
|
|
||||||
}),
|
|
||||||
expected_path,
|
|
||||||
location: Location::caller(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn __checked_vcd_output_macro_helper<T: BundleType>(
|
|
||||||
sim: &mut Simulation<T>,
|
|
||||||
cargo_manifest_dir: &'static str,
|
|
||||||
path: &'static str,
|
|
||||||
) -> Self {
|
|
||||||
Self::new(sim, Path::new(cargo_manifest_dir).join(path))
|
|
||||||
}
|
|
||||||
pub fn with_vcd_output<R>(&self, f: impl FnOnce(&str) -> R) -> R {
|
|
||||||
let Some(writer) = &self.writer else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
writer.clone().borrow(|output| {
|
|
||||||
let Ok(output) = str::from_utf8(output) else {
|
|
||||||
unreachable!("VcdWriter writes valid UTF-8");
|
|
||||||
};
|
|
||||||
f(output)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn finish(mut self) {
|
|
||||||
let Ok(()) = self.finish_impl(|msg| panic!("{msg}"));
|
|
||||||
}
|
|
||||||
fn finish_impl<E>(
|
|
||||||
&mut self,
|
|
||||||
error: impl FnOnce(std::fmt::Arguments<'_>) -> E,
|
|
||||||
) -> Result<(), E> {
|
|
||||||
let Self {
|
|
||||||
writer: Some(writer),
|
|
||||||
expected_path,
|
|
||||||
expected_contents,
|
|
||||||
location,
|
|
||||||
} = self
|
|
||||||
else {
|
|
||||||
// already finished
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let Ok(vcd) = String::from_utf8(writer.take()) else {
|
|
||||||
unreachable!("VcdWriter writes valid UTF-8");
|
|
||||||
};
|
|
||||||
let expected_path_d = expected_path.display();
|
|
||||||
if expected_contents
|
|
||||||
.as_ref()
|
|
||||||
.is_ok_and(|expected_contents| *expected_contents == vcd)
|
|
||||||
{
|
|
||||||
// avoid written output from being split from threads interleaving writes to stdout
|
|
||||||
let _stdout = std::io::stderr().lock();
|
|
||||||
// use println to get output captured by tests
|
|
||||||
println!("\n{location}: generated VCD matches the expected VCD in {expected_path_d}");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
// avoid written output from being split from threads interleaving writes to stderr
|
|
||||||
let _stderr = std::io::stderr().lock();
|
|
||||||
let error = |msg: std::fmt::Arguments<'_>| {
|
|
||||||
// print msg at both beginning and end so it's easier to find when the vcd is huge
|
|
||||||
Err(error(format_args!(
|
|
||||||
"\n{msg}####### VCD:\n{vcd}\n#######\n{msg}"
|
|
||||||
)))
|
|
||||||
};
|
|
||||||
let error = |msg: std::fmt::Arguments<'_>| match &*expected_contents {
|
|
||||||
Ok(_) => error(format_args!(
|
|
||||||
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
|
||||||
{msg}",
|
|
||||||
)),
|
|
||||||
Err((Some(current_dir), e)) => error(format_args!(
|
|
||||||
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
|
||||||
error: failed to read: {e}\n\
|
|
||||||
current dir: {current_dir}\n\
|
|
||||||
{msg}",
|
|
||||||
current_dir = current_dir.display(),
|
|
||||||
)),
|
|
||||||
Err((None, e)) => error(format_args!(
|
|
||||||
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
|
||||||
error: failed to read: {e}\n\
|
|
||||||
{msg}",
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
const OVERWRITE_VAR_NAME: &str = "OVERWRITE_EXPECTED_VCD";
|
|
||||||
const OVERWRITE_VAR_VALUE: &str = "overwrite";
|
|
||||||
match std::env::var_os(OVERWRITE_VAR_NAME) {
|
|
||||||
Some(v) if v == OVERWRITE_VAR_VALUE => match std::fs::write(&expected_path, &vcd) {
|
|
||||||
Ok(()) => error(format_args!(
|
|
||||||
"warning: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- writing the generated VCD to {expected_path_d}\n"
|
|
||||||
)),
|
|
||||||
Err(e) => error(format_args!(
|
|
||||||
"error: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- tried to write the generated VCD to {expected_path_d}\n\
|
|
||||||
error: failed to write: {e}"
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
_ => error(format_args!(
|
|
||||||
"note: rerun the test with the environment variable `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}`\n\
|
|
||||||
to update the expected output to match the generated output.\n"
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for CheckedVcdOutput {
|
|
||||||
#[track_caller]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = self.finish_impl(|msg| {
|
|
||||||
if std::thread::panicking() {
|
|
||||||
eprintln!("{msg}"); // use eprintln to get output captured by tests
|
|
||||||
} else {
|
|
||||||
panic!("{msg}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
/// Use in tests to check that [`Simulation`] generates the expected VCD traces, by comparing to a `.vcd` file containing the expected traces.
|
|
||||||
///
|
|
||||||
/// Use like so:
|
|
||||||
/// ```
|
|
||||||
/// # use fayalite::prelude::*;
|
|
||||||
/// #
|
|
||||||
/// # #[hdl_module]
|
|
||||||
/// # fn my_module() {
|
|
||||||
/// # #[hdl]
|
|
||||||
/// # let a: UInt<8> = m.input();
|
|
||||||
/// # #[hdl]
|
|
||||||
/// # let b: UInt<8> = m.output();
|
|
||||||
/// # connect(b, 0u8);
|
|
||||||
/// # #[hdl]
|
|
||||||
/// # if a.cmp_eq(100u8) {
|
|
||||||
/// # connect(b, 42u8);
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// // inside your #[test] fn my_test():
|
|
||||||
///
|
|
||||||
/// // get the module to simulate:
|
|
||||||
/// let m = my_module();
|
|
||||||
/// // create a simulation of the module:
|
|
||||||
/// let mut sim = Simulation::new(m);
|
|
||||||
/// // set up the expected VCD traces, the given .vcd path is relative to env!("CARGO_MANIFEST_DIR")
|
|
||||||
/// let _checked_vcd_output = checked_vcd_output!(
|
|
||||||
/// &mut sim,
|
|
||||||
/// "tests/expected/my_test.vcd",
|
|
||||||
/// );
|
|
||||||
/// // now run the simulation like normal:
|
|
||||||
/// sim.write(sim.io().a, 0u8);
|
|
||||||
/// assert_eq!(sim.read(sim.io().b).as_int(), 0);
|
|
||||||
/// sim.advance_time(SimDuration::from_micros(1));
|
|
||||||
/// sim.write(sim.io().a, 100u8);
|
|
||||||
/// assert_eq!(sim.read(sim.io().b).as_int(), 42);
|
|
||||||
/// ```
|
|
||||||
macro_rules! checked_vcd_output {
|
|
||||||
($sim:expr, $path_relative_to_manifest_dir:expr $(,)?) => {
|
|
||||||
$crate::testing::CheckedVcdOutput::__checked_vcd_output_macro_helper(
|
|
||||||
$sim,
|
|
||||||
$crate::__std::env!("CARGO_MANIFEST_DIR"),
|
|
||||||
$crate::__std::concat!($path_relative_to_manifest_dir),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use checked_vcd_output;
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -12,8 +12,7 @@ use crate::{
|
||||||
prelude::PhantomConst,
|
prelude::PhantomConst,
|
||||||
reset::{AsyncReset, Reset, SyncReset},
|
reset::{AsyncReset, Reset, SyncReset},
|
||||||
sim::value::DynSimOnly,
|
sim::value::DynSimOnly,
|
||||||
ty::{BaseType, CanonicalType, TraceAsString, TraceAsStringTrait},
|
ty::{BaseType, CanonicalType},
|
||||||
util::serde_by_id::SerdeById,
|
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
|
@ -39,7 +38,6 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for SerdePhantomConst<
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename = "CanonicalType")]
|
#[serde(rename = "CanonicalType")]
|
||||||
#[expect(private_interfaces)]
|
|
||||||
pub(crate) enum SerdeCanonicalType<
|
pub(crate) enum SerdeCanonicalType<
|
||||||
ArrayElement = CanonicalType,
|
ArrayElement = CanonicalType,
|
||||||
ThePhantomConst = SerdePhantomConst<Interned<PhantomConstCanonicalValue>>,
|
ThePhantomConst = SerdePhantomConst<Interned<PhantomConstCanonicalValue>>,
|
||||||
|
|
@ -67,10 +65,6 @@ pub(crate) enum SerdeCanonicalType<
|
||||||
Clock,
|
Clock,
|
||||||
PhantomConst(ThePhantomConst),
|
PhantomConst(ThePhantomConst),
|
||||||
DynSimOnly(DynSimOnly),
|
DynSimOnly(DynSimOnly),
|
||||||
TraceAsString {
|
|
||||||
inner_ty: Interned<CanonicalType>,
|
|
||||||
trace_as_string: SerdeById<Interned<dyn TraceAsStringTrait>>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> {
|
impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> {
|
||||||
|
|
@ -88,7 +82,6 @@ impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomCo
|
||||||
Self::Clock => "a Clock",
|
Self::Clock => "a Clock",
|
||||||
Self::PhantomConst(_) => "a PhantomConst",
|
Self::PhantomConst(_) => "a PhantomConst",
|
||||||
Self::DynSimOnly(_) => "a SimOnlyValue",
|
Self::DynSimOnly(_) => "a SimOnlyValue",
|
||||||
Self::TraceAsString { .. } => "a TraceAsString",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,15 +109,6 @@ impl<T: BaseType> From<T> for SerdeCanonicalType {
|
||||||
CanonicalType::Clock(Clock {}) => Self::Clock,
|
CanonicalType::Clock(Clock {}) => Self::Clock,
|
||||||
CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())),
|
CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())),
|
||||||
CanonicalType::DynSimOnly(ty) => Self::DynSimOnly(ty),
|
CanonicalType::DynSimOnly(ty) => Self::DynSimOnly(ty),
|
||||||
CanonicalType::TraceAsString(TraceAsString {
|
|
||||||
inner_ty,
|
|
||||||
trace_as_string,
|
|
||||||
}) => Self::TraceAsString {
|
|
||||||
inner_ty: inner_ty.interned(),
|
|
||||||
trace_as_string: SerdeById {
|
|
||||||
inner: trace_as_string.interned(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,16 +127,9 @@ impl From<SerdeCanonicalType> for CanonicalType {
|
||||||
SerdeCanonicalType::Reset => Self::Reset(Reset),
|
SerdeCanonicalType::Reset => Self::Reset(Reset),
|
||||||
SerdeCanonicalType::Clock => Self::Clock(Clock),
|
SerdeCanonicalType::Clock => Self::Clock(Clock),
|
||||||
SerdeCanonicalType::PhantomConst(value) => {
|
SerdeCanonicalType::PhantomConst(value) => {
|
||||||
Self::PhantomConst(PhantomConst::new_interned(value.0))
|
Self::PhantomConst(PhantomConst::new(value.0))
|
||||||
}
|
}
|
||||||
SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value),
|
SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value),
|
||||||
SerdeCanonicalType::TraceAsString {
|
|
||||||
inner_ty,
|
|
||||||
trace_as_string,
|
|
||||||
} => Self::TraceAsString(TraceAsString {
|
|
||||||
inner_ty: crate::intern::LazyInterned::Interned(inner_ty),
|
|
||||||
trace_as_string: crate::intern::LazyInterned::Interned(trace_as_string.inner),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher;
|
||||||
#[cfg(not(feature = "unstable-test-hasher"))]
|
#[cfg(not(feature = "unstable-test-hasher"))]
|
||||||
pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder;
|
pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder;
|
||||||
|
|
||||||
pub(crate) type HashMap<K, V, H = DefaultBuildHasher> = hashbrown::HashMap<K, V, H>;
|
pub(crate) type HashMap<K, V> = hashbrown::HashMap<K, V, DefaultBuildHasher>;
|
||||||
pub(crate) type HashSet<T, H = DefaultBuildHasher> = hashbrown::HashSet<T, H>;
|
pub(crate) type HashSet<T> = hashbrown::HashSet<T, DefaultBuildHasher>;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
||||||
|
|
@ -33,21 +33,13 @@ pub use const_cmp::{
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use scoped_ref::ScopedRef;
|
pub use scoped_ref::ScopedRef;
|
||||||
|
|
||||||
|
pub(crate) use misc::chain;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use misc::{
|
pub use misc::{
|
||||||
BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter,
|
BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, interned_bit,
|
||||||
SerdeJsonEscapeIf, SerdeJsonEscapeIfFormatter, SerdeJsonEscapeIfTest,
|
iter_eq_by, slice_range, try_slice_range,
|
||||||
SerdeJsonEscapeIfTestResult, interned_bit, iter_eq_by, os_str_strip_prefix,
|
|
||||||
os_str_strip_suffix, serialize_to_json_ascii, serialize_to_json_ascii_pretty,
|
|
||||||
serialize_to_json_ascii_pretty_with_indent, slice_range, try_slice_range,
|
|
||||||
};
|
};
|
||||||
pub(crate) use misc::{InternedStrCompareAsStr, chain, copy_le_bytes_to_bitslice};
|
|
||||||
|
|
||||||
pub mod bool_fixed_point_solver;
|
|
||||||
pub(crate) mod indented_print;
|
|
||||||
pub mod job_server;
|
pub mod job_server;
|
||||||
pub mod map_trait;
|
|
||||||
pub mod prefix_sum;
|
pub mod prefix_sum;
|
||||||
pub mod ready_valid;
|
pub mod ready_valid;
|
||||||
pub(crate) mod serde_by_id;
|
|
||||||
pub mod union_find_map;
|
|
||||||
|
|
|
||||||
|
|
@ -1,711 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use petgraph::unionfind::UnionFind;
|
|
||||||
use std::{collections::BTreeSet, fmt};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct Variable(usize);
|
|
||||||
|
|
||||||
impl Variable {
|
|
||||||
pub fn index(self) -> usize {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Variable {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt::Display::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Variable {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "v{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
||||||
pub enum Constraint {
|
|
||||||
/// `variable` is constrained to be [`!solver.unconstrained_variables_value()`](BoolFixedPointSolver::unconstrained_variables_value())
|
|
||||||
MaximallyConstrained { variable: Variable },
|
|
||||||
/// the constraint is `dest == src`
|
|
||||||
Equal { dest: Variable, src: Variable },
|
|
||||||
/// the constraint is `dest == dest & src`
|
|
||||||
And { dest: Variable, src: Variable },
|
|
||||||
/// the constraint is `dest == dest | src`
|
|
||||||
Or { dest: Variable, src: Variable },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
/// the constraint is `dest == dest & src`
|
|
||||||
struct AndConstraint {
|
|
||||||
dest: Variable,
|
|
||||||
src: Variable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AndConstraint {
|
|
||||||
fn from_or_constraint(or_constraint_dest: Variable, or_constraint_src: Variable) -> Self {
|
|
||||||
// `a == a | b` is equivalent to `b == b & a`
|
|
||||||
Self {
|
|
||||||
dest: or_constraint_src,
|
|
||||||
src: or_constraint_dest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for AndConstraint {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let Self { dest, src } = *self;
|
|
||||||
write!(f, "{dest} == {dest} & {src}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct BoolFixedPointSolver {
|
|
||||||
variables_union_find: UnionFind<usize>,
|
|
||||||
variables_value: Vec<bool>,
|
|
||||||
maximally_constrained: Vec<bool>,
|
|
||||||
unconstrained_variables_value: bool,
|
|
||||||
solved: bool,
|
|
||||||
and_constraints: BTreeSet<AndConstraint>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for BoolFixedPointSolver {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let Self {
|
|
||||||
variables_union_find,
|
|
||||||
variables_value,
|
|
||||||
maximally_constrained,
|
|
||||||
unconstrained_variables_value,
|
|
||||||
solved,
|
|
||||||
and_constraints,
|
|
||||||
} = self;
|
|
||||||
f.debug_struct("BoolFixedPointSolver")
|
|
||||||
.field(
|
|
||||||
"variables_union_find",
|
|
||||||
&fmt::from_fn(|f| {
|
|
||||||
f.debug_map()
|
|
||||||
.entries(
|
|
||||||
(0..variables_union_find.len())
|
|
||||||
.map(|i| (Variable(i), Variable(variables_union_find.find(i)))),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.field(
|
|
||||||
"variables_value",
|
|
||||||
&fmt::from_fn(|f| {
|
|
||||||
let mut debug_map = f.debug_map();
|
|
||||||
for (i, v) in variables_value.iter().enumerate() {
|
|
||||||
if variables_union_find.find(i) == i {
|
|
||||||
debug_map.entry(&Variable(i), v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug_map.finish()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.field(
|
|
||||||
"maximally_constrained",
|
|
||||||
&fmt::from_fn(|f| {
|
|
||||||
let mut debug_map = f.debug_map();
|
|
||||||
for (i, v) in maximally_constrained.iter().enumerate() {
|
|
||||||
if variables_union_find.find(i) == i {
|
|
||||||
debug_map.entry(&Variable(i), v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug_map.finish()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.field(
|
|
||||||
"unconstrained_variables_value",
|
|
||||||
unconstrained_variables_value,
|
|
||||||
)
|
|
||||||
.field("solved", solved)
|
|
||||||
.field("and_constraints", and_constraints)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BoolFixedPointSolver {
|
|
||||||
pub const fn new(unconstrained_variables_value: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
variables_union_find: UnionFind::new_empty(),
|
|
||||||
variables_value: Vec::new(),
|
|
||||||
maximally_constrained: Vec::new(),
|
|
||||||
unconstrained_variables_value,
|
|
||||||
solved: false,
|
|
||||||
and_constraints: BTreeSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn unconstrained_variables_value(&self) -> bool {
|
|
||||||
self.unconstrained_variables_value
|
|
||||||
}
|
|
||||||
pub fn new_variable(&mut self) -> Variable {
|
|
||||||
let index = self.variables_union_find.new_set();
|
|
||||||
self.variables_value
|
|
||||||
.push(self.unconstrained_variables_value);
|
|
||||||
self.maximally_constrained.push(false);
|
|
||||||
self.solved = false;
|
|
||||||
Variable(index)
|
|
||||||
}
|
|
||||||
pub fn variable_count(&self) -> usize {
|
|
||||||
self.variables_union_find.len()
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn assert_variable_in_range(&self, variable: Variable) {
|
|
||||||
if variable.0 >= self.variable_count() {
|
|
||||||
panic!("invalid variable {variable:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn add_constraint(&mut self, constraint: Constraint) {
|
|
||||||
self.solved = false;
|
|
||||||
match constraint {
|
|
||||||
Constraint::MaximallyConstrained { variable } => {
|
|
||||||
self.assert_variable_in_range(variable);
|
|
||||||
self.maximally_constrained[self.variables_union_find.find_mut(variable.0)] = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Constraint::Equal { dest, src } => {
|
|
||||||
self.assert_variable_in_range(dest);
|
|
||||||
self.assert_variable_in_range(src);
|
|
||||||
let maximally_constrained = self.maximally_constrained
|
|
||||||
[self.variables_union_find.find_mut(dest.0)]
|
|
||||||
| self.maximally_constrained[self.variables_union_find.find_mut(src.0)];
|
|
||||||
self.variables_union_find.union(dest.0, src.0);
|
|
||||||
let merged_index = self.variables_union_find.find_mut(dest.0);
|
|
||||||
self.maximally_constrained[merged_index] = maximally_constrained;
|
|
||||||
}
|
|
||||||
Constraint::And { dest, src } => {
|
|
||||||
self.assert_variable_in_range(src);
|
|
||||||
self.assert_variable_in_range(dest);
|
|
||||||
if src != dest {
|
|
||||||
self.and_constraints.insert(AndConstraint { dest, src });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Constraint::Or { dest, src } => {
|
|
||||||
self.assert_variable_in_range(src);
|
|
||||||
self.assert_variable_in_range(dest);
|
|
||||||
if src != dest {
|
|
||||||
self.and_constraints
|
|
||||||
.insert(AndConstraint::from_or_constraint(dest, src));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn solve(&mut self) {
|
|
||||||
for (value, maximally_constrained) in self
|
|
||||||
.variables_value
|
|
||||||
.iter_mut()
|
|
||||||
.zip(&self.maximally_constrained)
|
|
||||||
{
|
|
||||||
*value = self.unconstrained_variables_value ^ *maximally_constrained;
|
|
||||||
}
|
|
||||||
let mut variables_to_constraints_map: Vec<Vec<AndConstraint>> =
|
|
||||||
vec![Vec::new(); self.variable_count()];
|
|
||||||
for &AndConstraint { mut dest, mut src } in &self.and_constraints {
|
|
||||||
dest.0 = self.variables_union_find.find_mut(dest.0);
|
|
||||||
src.0 = self.variables_union_find.find_mut(src.0);
|
|
||||||
if dest == src {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let constraint = AndConstraint { dest, src };
|
|
||||||
variables_to_constraints_map[dest.0].push(constraint);
|
|
||||||
variables_to_constraints_map[src.0].push(constraint);
|
|
||||||
}
|
|
||||||
let mut worklist: Vec<Variable> = (0..self.variable_count())
|
|
||||||
.filter(|&index| self.variables_union_find.find_mut(index) == index)
|
|
||||||
.map(Variable)
|
|
||||||
.collect();
|
|
||||||
while let Some(variable) = worklist.pop() {
|
|
||||||
for &AndConstraint { dest, src } in &variables_to_constraints_map[variable.0] {
|
|
||||||
let dest_value = self.variables_value[dest.0];
|
|
||||||
let src_value = self.variables_value[src.0];
|
|
||||||
// equivalent to `dest_value != dest_value & src_value`:
|
|
||||||
let is_unsatisfied = dest_value && !src_value;
|
|
||||||
if is_unsatisfied {
|
|
||||||
if self.unconstrained_variables_value {
|
|
||||||
self.variables_value[dest.0] = false;
|
|
||||||
worklist.push(dest);
|
|
||||||
} else {
|
|
||||||
self.variables_value[src.0] = true;
|
|
||||||
worklist.push(src);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.solved = true;
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn value(&mut self, variable: Variable) -> bool {
|
|
||||||
#[cold]
|
|
||||||
fn solve_cold(this: &mut BoolFixedPointSolver) {
|
|
||||||
this.solve();
|
|
||||||
}
|
|
||||||
self.assert_variable_in_range(variable);
|
|
||||||
if !self.solved {
|
|
||||||
solve_cold(self);
|
|
||||||
}
|
|
||||||
self.variables_value[self.variables_union_find.find_mut(variable.0)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::num::NonZero;
|
|
||||||
|
|
||||||
struct TestCase<'a, C, Vars, Vals> {
|
|
||||||
variable_count: usize,
|
|
||||||
expected_values: Option<&'a [bool]>,
|
|
||||||
constraints: C,
|
|
||||||
variables: Vars,
|
|
||||||
values: Vals,
|
|
||||||
solver: BoolFixedPointSolver,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, C: FnOnce(&[Variable]) -> I, I: IntoIterator<Item = Constraint>> TestCase<'a, C, (), ()> {
|
|
||||||
fn new_expected(
|
|
||||||
unconstrained_variables_value: bool,
|
|
||||||
expected_values: &'a [bool],
|
|
||||||
constraints: C,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
variable_count: expected_values.len(),
|
|
||||||
expected_values: Some(expected_values),
|
|
||||||
constraints,
|
|
||||||
variables: (),
|
|
||||||
values: (),
|
|
||||||
solver: BoolFixedPointSolver::new(unconstrained_variables_value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn get_constraints_and_variables(
|
|
||||||
self,
|
|
||||||
) -> TestCase<'a, Vec<Constraint>, Vec<Variable>, [bool; 0]> {
|
|
||||||
let Self {
|
|
||||||
variable_count,
|
|
||||||
expected_values,
|
|
||||||
constraints,
|
|
||||||
variables: (),
|
|
||||||
values: (),
|
|
||||||
mut solver,
|
|
||||||
} = self;
|
|
||||||
assert_eq!(
|
|
||||||
expected_values.map_or(variable_count, |v| v.len()),
|
|
||||||
variable_count,
|
|
||||||
);
|
|
||||||
let variables = Vec::from_iter((0..variable_count).map(|_| solver.new_variable()));
|
|
||||||
let constraints = Vec::from_iter(constraints(&variables));
|
|
||||||
TestCase {
|
|
||||||
variable_count,
|
|
||||||
expected_values,
|
|
||||||
constraints,
|
|
||||||
variables,
|
|
||||||
values: [],
|
|
||||||
solver,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TestCase<'a, Vec<Constraint>, Vec<Variable>, [bool; 0]> {
|
|
||||||
#[track_caller]
|
|
||||||
fn add_and_check_constraints(&mut self) {
|
|
||||||
if let Some(expected_values) = self.expected_values {
|
|
||||||
self.check_constraints("expected values", expected_values);
|
|
||||||
}
|
|
||||||
for &constraint in &self.constraints {
|
|
||||||
self.solver.add_constraint(constraint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn get_values(self) -> TestCase<'a, Vec<Constraint>, Vec<Variable>, Vec<bool>> {
|
|
||||||
let Self {
|
|
||||||
variable_count,
|
|
||||||
expected_values,
|
|
||||||
constraints,
|
|
||||||
variables,
|
|
||||||
values: [],
|
|
||||||
mut solver,
|
|
||||||
} = self;
|
|
||||||
let values = Vec::from_iter(variables.iter().map(|&v| solver.value(v)));
|
|
||||||
TestCase {
|
|
||||||
variable_count,
|
|
||||||
expected_values,
|
|
||||||
constraints,
|
|
||||||
variables,
|
|
||||||
values,
|
|
||||||
solver,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TestCase<'a, Vec<Constraint>, Vec<Variable>, Vec<bool>> {
|
|
||||||
#[track_caller]
|
|
||||||
fn check_values(&self) {
|
|
||||||
let Self {
|
|
||||||
variable_count: _,
|
|
||||||
expected_values,
|
|
||||||
constraints: _,
|
|
||||||
variables,
|
|
||||||
values,
|
|
||||||
solver: _,
|
|
||||||
} = self;
|
|
||||||
if let Some(expected_values) = expected_values {
|
|
||||||
for ((&expected_value, &variable), &value) in
|
|
||||||
expected_values.iter().zip(variables).zip(values)
|
|
||||||
{
|
|
||||||
if expected_value != value {
|
|
||||||
self.error(format_args!(
|
|
||||||
"solver output for {variable} of {value:?} doesn't \
|
|
||||||
match expected value of {expected_value:?}",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.check_constraints("solved values", values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Vals: AsRef<[bool]>> TestCase<'a, Vec<Constraint>, Vec<Variable>, Vals> {
|
|
||||||
#[track_caller]
|
|
||||||
fn check_constraints(&self, values_name: &str, values: &[bool]) {
|
|
||||||
let unconstrained_variables_value = self.solver.unconstrained_variables_value();
|
|
||||||
let v = |variable: Variable| values[variable.index()];
|
|
||||||
for &constraint in &self.constraints {
|
|
||||||
let satisfied = match constraint {
|
|
||||||
Constraint::MaximallyConstrained { variable } => {
|
|
||||||
v(variable) != unconstrained_variables_value
|
|
||||||
}
|
|
||||||
Constraint::Equal { dest, src } => v(dest) == v(src),
|
|
||||||
Constraint::And { dest, src } => v(dest) == v(dest) & v(src),
|
|
||||||
Constraint::Or { dest, src } => v(dest) == v(dest) | v(src),
|
|
||||||
};
|
|
||||||
if !satisfied {
|
|
||||||
self.error(format_args!(
|
|
||||||
"{values_name} don't satisfy constraint: {constraint:#?}"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn error(&self, msg: fmt::Arguments<'_>) -> ! {
|
|
||||||
let Self {
|
|
||||||
variable_count,
|
|
||||||
expected_values,
|
|
||||||
ref constraints,
|
|
||||||
ref variables,
|
|
||||||
ref values,
|
|
||||||
ref solver,
|
|
||||||
} = *self;
|
|
||||||
let values = values.as_ref();
|
|
||||||
panic!(
|
|
||||||
"{msg}\n\
|
|
||||||
values={values:#?}\n\
|
|
||||||
constraints={constraints:#?}\n\
|
|
||||||
solver={solver:#?}",
|
|
||||||
values = fmt::from_fn(|f| {
|
|
||||||
let mut debug_map = f.debug_map();
|
|
||||||
for i in 0..variable_count {
|
|
||||||
debug_map.key(&variables[i]);
|
|
||||||
if let Some(value) = values.get(i) {
|
|
||||||
if let Some(expected_values) = expected_values {
|
|
||||||
debug_map.value(&format_args!(
|
|
||||||
"{value:?} (expected: {:?})",
|
|
||||||
expected_values[i],
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
debug_map.value(value);
|
|
||||||
}
|
|
||||||
} else if let Some(expected_values) = expected_values {
|
|
||||||
debug_map.value(&format_args!("(expected: {:?})", expected_values[i]));
|
|
||||||
} else {
|
|
||||||
debug_map.value(&format_args!("None"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug_map.finish()
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn test_case<I: IntoIterator<Item = Constraint>>(
|
|
||||||
test_case: TestCase<'_, impl FnOnce(&[Variable]) -> I, (), ()>,
|
|
||||||
) {
|
|
||||||
let mut test_case = test_case.get_constraints_and_variables();
|
|
||||||
test_case.add_and_check_constraints();
|
|
||||||
let test_case = test_case.get_values();
|
|
||||||
test_case.check_values();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_simple() {
|
|
||||||
test_case(TestCase::new_expected(false, &[], |_| []));
|
|
||||||
test_case(TestCase::new_expected(true, &[], |_| []));
|
|
||||||
test_case(TestCase::new_expected(false, &[false], |_| []));
|
|
||||||
test_case(TestCase::new_expected(true, &[true], |_| []));
|
|
||||||
test_case(TestCase::new_expected(false, &[true], |v| {
|
|
||||||
[Constraint::MaximallyConstrained { variable: v[0] }]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(true, &[false], |v| {
|
|
||||||
[Constraint::MaximallyConstrained { variable: v[0] }]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(false, &[true, true], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::Equal {
|
|
||||||
dest: v[1],
|
|
||||||
src: v[0],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(true, &[false, false], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::Equal {
|
|
||||||
dest: v[1],
|
|
||||||
src: v[0],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(false, &[true, false], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::And {
|
|
||||||
dest: v[1],
|
|
||||||
src: v[0],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(true, &[false, false], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::And {
|
|
||||||
dest: v[1],
|
|
||||||
src: v[0],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(false, &[true, true], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::And {
|
|
||||||
dest: v[0],
|
|
||||||
src: v[1],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(true, &[false, true], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::And {
|
|
||||||
dest: v[0],
|
|
||||||
src: v[1],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(false, &[true, true], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::Or {
|
|
||||||
dest: v[1],
|
|
||||||
src: v[0],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(true, &[false, true], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::Or {
|
|
||||||
dest: v[1],
|
|
||||||
src: v[0],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(false, &[true, false], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::Or {
|
|
||||||
dest: v[0],
|
|
||||||
src: v[1],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
test_case(TestCase::new_expected(true, &[false, false], |v| {
|
|
||||||
[
|
|
||||||
Constraint::MaximallyConstrained { variable: v[0] },
|
|
||||||
Constraint::Or {
|
|
||||||
dest: v[0],
|
|
||||||
src: v[1],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Rng {
|
|
||||||
state: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rng {
|
|
||||||
fn new(test_case_index: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
state: (test_case_index as u64) << 32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn next_u64(&mut self) -> u64 {
|
|
||||||
self.state += 1;
|
|
||||||
// 4 random primes and 4 random rotate amounts
|
|
||||||
self.state
|
|
||||||
.wrapping_mul(0xA3C7_8807_EA6D_A4F9)
|
|
||||||
.rotate_left(43)
|
|
||||||
.wrapping_mul(0x1CCA_797A_6BF8_8C63)
|
|
||||||
.rotate_left(8)
|
|
||||||
.wrapping_mul(0xCC50_AA59_7C41_946F)
|
|
||||||
.rotate_left(12)
|
|
||||||
.wrapping_mul(0xFB2A_0137_F878_C4B5)
|
|
||||||
.rotate_left(58)
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn next_u64_in_range(&mut self, range: std::ops::Range<u64>) -> u64 {
|
|
||||||
let Some(len) = range.end.checked_sub(range.start).and_then(NonZero::new) else {
|
|
||||||
panic!("empty range: {range:?}");
|
|
||||||
};
|
|
||||||
let max_quotient = u64::MAX / len;
|
|
||||||
loop {
|
|
||||||
let next_u64 = self.next_u64();
|
|
||||||
let quotient = next_u64 / len;
|
|
||||||
let remainder = next_u64 % len;
|
|
||||||
if quotient < max_quotient {
|
|
||||||
return remainder + range.start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn next_usize_in_range(&mut self, range: std::ops::Range<usize>) -> usize {
|
|
||||||
self.next_u64_in_range(range.start as u64..range.end as u64) as usize
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
fn next_from_slice<'a, T>(&mut self, slice: &'a [T]) -> &'a T {
|
|
||||||
assert!(!slice.is_empty());
|
|
||||||
&slice[self.next_usize_in_range(0..slice.len())]
|
|
||||||
}
|
|
||||||
fn next_bool(&mut self) -> bool {
|
|
||||||
(self.next_u64() & 1) != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn test_bool_fixed_point_solver_random_case(test_case_index: u32) {
|
|
||||||
println!("test_bool_fixed_point_solver_random_case({test_case_index})");
|
|
||||||
let mut rng = Rng::new(test_case_index);
|
|
||||||
// bias towards smaller problems to make them easier to debug
|
|
||||||
let variable_count = rng
|
|
||||||
.next_u64_in_range(1..1_000_000)
|
|
||||||
.pow(2)
|
|
||||||
.div_ceil(1_000_000_000) as usize;
|
|
||||||
let constraint_count =
|
|
||||||
rng.next_usize_in_range(0..(variable_count * variable_count).clamp(0, 10000));
|
|
||||||
let solver = BoolFixedPointSolver::new(rng.next_bool());
|
|
||||||
test_case(TestCase {
|
|
||||||
variable_count,
|
|
||||||
expected_values: None,
|
|
||||||
constraints: |variables: &[Variable]| {
|
|
||||||
Vec::from_iter(
|
|
||||||
(0..constraint_count).map(|_| match rng.next_usize_in_range(0..4) {
|
|
||||||
0 => Constraint::MaximallyConstrained {
|
|
||||||
variable: *rng.next_from_slice(variables),
|
|
||||||
},
|
|
||||||
1 => Constraint::Equal {
|
|
||||||
dest: *rng.next_from_slice(variables),
|
|
||||||
src: *rng.next_from_slice(variables),
|
|
||||||
},
|
|
||||||
2 => Constraint::And {
|
|
||||||
dest: *rng.next_from_slice(variables),
|
|
||||||
src: *rng.next_from_slice(variables),
|
|
||||||
},
|
|
||||||
3 => Constraint::Or {
|
|
||||||
dest: *rng.next_from_slice(variables),
|
|
||||||
src: *rng.next_from_slice(variables),
|
|
||||||
},
|
|
||||||
4.. => unreachable!(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
variables: (),
|
|
||||||
values: (),
|
|
||||||
solver,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const CASES_FULL_RANGE: std::ops::Range<u32> = 0..100_000;
|
|
||||||
|
|
||||||
fn mul_div(v: u32, factor: u32, divisor: u32) -> u32 {
|
|
||||||
((v as u64 * factor as u64) / divisor as u64) as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases(split_index: u32) {
|
|
||||||
assert!(split_index < CASES_SPLIT_COUNT);
|
|
||||||
let full_range_len = CASES_FULL_RANGE.end - CASES_FULL_RANGE.start;
|
|
||||||
let start = mul_div(split_index, full_range_len, CASES_SPLIT_COUNT);
|
|
||||||
let end = mul_div(split_index + 1, full_range_len, CASES_SPLIT_COUNT);
|
|
||||||
for test_case_index in start..end {
|
|
||||||
test_bool_fixed_point_solver_random_case(test_case_index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const CASES_SPLIT_COUNT: u32 = 10;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_0() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_1() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_2() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_3() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_4() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_5() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_6() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_7() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(7);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_8() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bool_fixed_point_solver_random_cases_9() {
|
|
||||||
test_bool_fixed_point_solver_random_cases(9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
fmt::{self, Write as _},
|
|
||||||
marker::PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IndentState {
|
|
||||||
indent: usize,
|
|
||||||
need_indent: bool,
|
|
||||||
buf: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static INDENT_STATE: std::cell::RefCell<IndentState> = const {
|
|
||||||
std::cell::RefCell::new(IndentState {
|
|
||||||
indent: 0,
|
|
||||||
need_indent: true,
|
|
||||||
buf: String::new(),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IndentedOut;
|
|
||||||
|
|
||||||
impl fmt::Write for IndentedOut {
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
INDENT_STATE.with_borrow_mut(|state| {
|
|
||||||
let IndentState {
|
|
||||||
indent,
|
|
||||||
need_indent,
|
|
||||||
buf,
|
|
||||||
} = state;
|
|
||||||
buf.clear();
|
|
||||||
for ch in s.chars() {
|
|
||||||
if ch == '\n' {
|
|
||||||
*need_indent = true;
|
|
||||||
} else {
|
|
||||||
if *need_indent {
|
|
||||||
*need_indent = false;
|
|
||||||
for _ in 0..*indent {
|
|
||||||
buf.push_str(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.push(ch)
|
|
||||||
}
|
|
||||||
std::print!("{buf}");
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) struct PushIndent(PhantomData<*const ()>);
|
|
||||||
|
|
||||||
impl Drop for PushIndent {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = INDENT_STATE.try_with(|state| state.borrow_mut().indent -= 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PushIndent {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
INDENT_STATE.with_borrow_mut(|state| state.indent += 1);
|
|
||||||
Self(PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn indented_print_fmt<const LN: bool>(args: fmt::Arguments<'_>) {
|
|
||||||
if LN {
|
|
||||||
writeln!(IndentedOut, "{args}").expect("writing can't fail")
|
|
||||||
} else {
|
|
||||||
IndentedOut.write_fmt(args).expect("writing can't fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
macro_rules! indented_print {
|
|
||||||
($($args:tt)*) => {
|
|
||||||
$crate::util::indented_print::indented_print_fmt::<false>($crate::__std::format_args!($($args)*))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) use indented_print;
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
macro_rules! indented_println {
|
|
||||||
($($args:tt)*) => {
|
|
||||||
$crate::util::indented_print::indented_print_fmt::<true>($crate::__std::format_args!($($args)*))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) use indented_println;
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
macro_rules! indented_dbg {
|
|
||||||
($expr:expr) => {{
|
|
||||||
let v = $expr;
|
|
||||||
$crate::util::indented_print::indented_println!(
|
|
||||||
"[{}:{}:{}] {} = {v:#?}",
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
stringify!($expr),
|
|
||||||
);
|
|
||||||
v
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) use indented_dbg;
|
|
||||||
|
|
@ -1,36 +1,26 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use ctor::{ctor, dtor};
|
use ctor::ctor;
|
||||||
use jobslot::Client;
|
use jobslot::{Acquired, Client};
|
||||||
use std::{
|
use std::{
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
io, mem,
|
mem,
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
sync::{Mutex, MutexGuard},
|
sync::{Condvar, Mutex, Once, OnceLock},
|
||||||
|
thread::spawn,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[ctor]
|
fn get_or_make_client() -> &'static Client {
|
||||||
static CLIENT: Mutex<Option<Option<Client>>> = unsafe { Mutex::new(Some(Client::from_env())) };
|
#[ctor]
|
||||||
|
static CLIENT: OnceLock<Client> = unsafe {
|
||||||
#[dtor]
|
match Client::from_env() {
|
||||||
fn drop_client() {
|
Some(client) => OnceLock::from(client),
|
||||||
drop(
|
None => OnceLock::new(),
|
||||||
match CLIENT.lock() {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => e.into_inner(),
|
|
||||||
}
|
}
|
||||||
.take(),
|
};
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_or_make_client() -> Client {
|
CLIENT.get_or_init(|| {
|
||||||
CLIENT
|
|
||||||
.lock()
|
|
||||||
.expect("shouldn't have panicked")
|
|
||||||
.as_mut()
|
|
||||||
.expect("shutting down")
|
|
||||||
.get_or_insert_with(|| {
|
|
||||||
let mut available_parallelism = None;
|
let mut available_parallelism = None;
|
||||||
let mut args = std::env::args_os().skip(1);
|
let mut args = std::env::args_os().skip(1);
|
||||||
while let Some(arg) = args.next() {
|
while let Some(arg) = args.next() {
|
||||||
|
|
@ -62,95 +52,141 @@ fn get_or_make_client() -> Client {
|
||||||
} else {
|
} else {
|
||||||
NonZeroUsize::new(1).unwrap()
|
NonZeroUsize::new(1).unwrap()
|
||||||
};
|
};
|
||||||
Client::new_with_fifo(available_parallelism.get() - 1)
|
Client::new_with_fifo(available_parallelism.get() - 1).expect("failed to create job server")
|
||||||
.expect("failed to create job server")
|
|
||||||
})
|
})
|
||||||
.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
obtained_count: usize,
|
|
||||||
waiting_count: usize,
|
waiting_count: usize,
|
||||||
|
available: Vec<Acquired>,
|
||||||
|
implicit_available: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn total_available(&self) -> usize {
|
||||||
|
self.available.len() + self.implicit_available as usize
|
||||||
|
}
|
||||||
|
fn additional_waiting(&self) -> usize {
|
||||||
|
self.waiting_count.saturating_sub(self.total_available())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static STATE: Mutex<State> = Mutex::new(State {
|
static STATE: Mutex<State> = Mutex::new(State {
|
||||||
obtained_count: 0,
|
|
||||||
waiting_count: 0,
|
waiting_count: 0,
|
||||||
|
available: Vec::new(),
|
||||||
|
implicit_available: true,
|
||||||
});
|
});
|
||||||
|
static COND_VAR: Condvar = Condvar::new();
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum AcquiredJobInner {
|
||||||
|
FromJobServer(Acquired),
|
||||||
|
ImplicitJob,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AcquiredJob {
|
pub struct AcquiredJob {
|
||||||
client: Client,
|
job: AcquiredJobInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AcquiredJob {
|
impl AcquiredJob {
|
||||||
pub fn acquire() -> io::Result<Self> {
|
fn start_acquire_thread() {
|
||||||
|
static STARTED_THREAD: Once = Once::new();
|
||||||
|
STARTED_THREAD.call_once(|| {
|
||||||
|
spawn(|| {
|
||||||
|
let mut acquired = None;
|
||||||
let client = get_or_make_client();
|
let client = get_or_make_client();
|
||||||
struct Waiting {}
|
|
||||||
|
|
||||||
impl Waiting {
|
|
||||||
fn done(self) -> MutexGuard<'static, State> {
|
|
||||||
mem::forget(self);
|
|
||||||
let mut state = STATE.lock().unwrap();
|
let mut state = STATE.lock().unwrap();
|
||||||
state.waiting_count -= 1;
|
loop {
|
||||||
|
state = if state.additional_waiting() == 0 {
|
||||||
|
if acquired.is_some() {
|
||||||
|
drop(state);
|
||||||
|
drop(acquired.take()); // drop Acquired outside of lock
|
||||||
|
STATE.lock().unwrap()
|
||||||
|
} else {
|
||||||
|
COND_VAR.wait(state).unwrap()
|
||||||
|
}
|
||||||
|
} else if acquired.is_some() {
|
||||||
|
// allocate space before moving Acquired to ensure we
|
||||||
|
// drop Acquired outside of the lock on panic
|
||||||
|
state.available.reserve(1);
|
||||||
|
state.available.push(acquired.take().unwrap());
|
||||||
|
COND_VAR.notify_all();
|
||||||
state
|
state
|
||||||
|
} else {
|
||||||
|
drop(state);
|
||||||
|
acquired = Some(
|
||||||
|
client
|
||||||
|
.acquire()
|
||||||
|
.expect("can't acquire token from job server"),
|
||||||
|
);
|
||||||
|
STATE.lock().unwrap()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
impl Drop for Waiting {
|
fn acquire_inner(block: bool) -> Option<Self> {
|
||||||
fn drop(&mut self) {
|
Self::start_acquire_thread();
|
||||||
STATE.lock().unwrap().waiting_count -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut state = STATE.lock().unwrap();
|
let mut state = STATE.lock().unwrap();
|
||||||
if state.obtained_count == 0 && state.waiting_count == 0 {
|
loop {
|
||||||
state.obtained_count = 1; // get implicit token
|
if let Some(acquired) = state.available.pop() {
|
||||||
return Ok(Self { client });
|
return Some(Self {
|
||||||
|
job: AcquiredJobInner::FromJobServer(acquired),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if state.implicit_available {
|
||||||
|
state.implicit_available = false;
|
||||||
|
return Some(Self {
|
||||||
|
job: AcquiredJobInner::ImplicitJob,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if !block {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
state.waiting_count += 1;
|
state.waiting_count += 1;
|
||||||
drop(state);
|
state = COND_VAR.wait(state).unwrap();
|
||||||
let waiting = Waiting {};
|
state.waiting_count -= 1;
|
||||||
client.acquire_raw()?;
|
}
|
||||||
state = waiting.done();
|
}
|
||||||
state.obtained_count = state
|
pub fn try_acquire() -> Option<Self> {
|
||||||
.obtained_count
|
Self::acquire_inner(false)
|
||||||
.checked_add(1)
|
}
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "obtained_count overflowed"))?;
|
pub fn acquire() -> Self {
|
||||||
drop(state);
|
Self::acquire_inner(true).expect("failed to acquire token")
|
||||||
Ok(Self { client })
|
|
||||||
}
|
}
|
||||||
pub fn run_command<R>(
|
pub fn run_command<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cmd: std::process::Command,
|
cmd: std::process::Command,
|
||||||
f: impl FnOnce(&mut std::process::Command) -> std::io::Result<R>,
|
f: impl FnOnce(&mut std::process::Command) -> std::io::Result<R>,
|
||||||
) -> std::io::Result<R> {
|
) -> std::io::Result<R> {
|
||||||
self.client.configure_make_and_run_with_fifo(cmd, f)
|
get_or_make_client().configure_make_and_run_with_fifo(cmd, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AcquiredJob {
|
impl Drop for AcquiredJob {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut state = STATE.lock().unwrap();
|
let mut state = STATE.lock().unwrap();
|
||||||
match &mut *state {
|
match &self.job {
|
||||||
State {
|
AcquiredJobInner::FromJobServer(_) => {
|
||||||
obtained_count: 0, ..
|
if state.waiting_count > state.available.len() + state.implicit_available as usize {
|
||||||
} => unreachable!(),
|
// allocate space before moving Acquired to ensure we
|
||||||
State {
|
// drop Acquired outside of the lock on panic
|
||||||
obtained_count: obtained_count @ 1,
|
state.available.reserve(1);
|
||||||
waiting_count,
|
let AcquiredJobInner::FromJobServer(acquired) =
|
||||||
} => {
|
mem::replace(&mut self.job, AcquiredJobInner::ImplicitJob)
|
||||||
*obtained_count = 0; // drop implicit token
|
else {
|
||||||
let any_waiting = *waiting_count != 0;
|
unreachable!()
|
||||||
drop(state);
|
};
|
||||||
if any_waiting {
|
state.available.push(acquired);
|
||||||
// we have the implicit token, but some other thread is trying to acquire a token,
|
COND_VAR.notify_all();
|
||||||
// release the implicit token so they can acquire it.
|
|
||||||
let _ = self.client.release_raw(); // we're in drop, just ignore errors since we at least tried
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State { obtained_count, .. } => {
|
AcquiredJobInner::ImplicitJob => {
|
||||||
*obtained_count = obtained_count.saturating_sub(1);
|
state.implicit_available = true;
|
||||||
drop(state);
|
if state.waiting_count > state.available.len() {
|
||||||
let _ = self.client.release_raw(); // we're in drop, just ignore errors since we at least tried
|
COND_VAR.notify_all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,463 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
pub enum Entry<'a, M: Map + 'a> {
|
|
||||||
Vacant(M::VacantEntry<'a>),
|
|
||||||
Occupied(M::OccupiedEntry<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, M: Map + 'a> Entry<'a, M> {
|
|
||||||
pub fn and_modify<F: FnOnce(&mut M::Value)>(mut self, f: F) -> Self {
|
|
||||||
if let Self::Occupied(entry) = &mut self {
|
|
||||||
f(entry.get_mut());
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn insert_entry(self, v: M::Value) -> M::OccupiedEntry<'a> {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => entry.insert_entry(v),
|
|
||||||
Self::Occupied(mut entry) => {
|
|
||||||
entry.insert(v);
|
|
||||||
entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn key(&self) -> &M::Key {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => entry.key(),
|
|
||||||
Self::Occupied(entry) => entry.key(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn or_default(self) -> &'a mut M::Value
|
|
||||||
where
|
|
||||||
M::Value: Default,
|
|
||||||
{
|
|
||||||
self.or_insert_with(Default::default)
|
|
||||||
}
|
|
||||||
pub fn or_insert(self, v: M::Value) -> &'a mut M::Value {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => entry.insert(v),
|
|
||||||
Self::Occupied(entry) => entry.into_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn or_insert_with<F: FnOnce() -> M::Value>(self, f: F) -> &'a mut M::Value {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => entry.insert(f()),
|
|
||||||
Self::Occupied(entry) => entry.into_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn or_insert_with_key<F: FnOnce(&M::Key) -> M::Value>(self, f: F) -> &'a mut M::Value {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => {
|
|
||||||
let v = f(entry.key());
|
|
||||||
entry.insert(v)
|
|
||||||
}
|
|
||||||
Self::Occupied(entry) => entry.into_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, M: Map<OccupiedEntry<'a>: fmt::Debug, VacantEntry<'a>: fmt::Debug> + 'a> fmt::Debug
|
|
||||||
for Entry<'a, M>
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(v) => f.debug_tuple("Vacant").field(v).finish(),
|
|
||||||
Self::Occupied(v) => f.debug_tuple("Occupied").field(v).finish(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait VacantEntry<'a>: Sized {
|
|
||||||
type Map: Map<VacantEntry<'a> = Self> + 'a;
|
|
||||||
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value;
|
|
||||||
fn insert_entry(self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::OccupiedEntry<'a>;
|
|
||||||
fn into_key(self) -> <Self::Map as Map>::Key;
|
|
||||||
fn key(&self) -> &<Self::Map as Map>::Key;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait OccupiedEntry<'a>: Sized {
|
|
||||||
type Map: Map<OccupiedEntry<'a> = Self> + 'a;
|
|
||||||
fn get(&self) -> &<Self::Map as Map>::Value;
|
|
||||||
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value;
|
|
||||||
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value;
|
|
||||||
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value;
|
|
||||||
fn key(&self) -> &<Self::Map as Map>::Key;
|
|
||||||
fn remove(self) -> <Self::Map as Map>::Value;
|
|
||||||
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Map:
|
|
||||||
Sized
|
|
||||||
+ IntoIterator<Item = (<Self as Map>::Key, <Self as Map>::Value)>
|
|
||||||
+ Extend<(<Self as Map>::Key, <Self as Map>::Value)>
|
|
||||||
+ FromIterator<(<Self as Map>::Key, <Self as Map>::Value)>
|
|
||||||
{
|
|
||||||
type Key;
|
|
||||||
type Value;
|
|
||||||
type IntoKeys: Iterator<Item = Self::Key>;
|
|
||||||
type IntoValues: Iterator<Item = Self::Value>;
|
|
||||||
type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type IterMut<'a>: Iterator<Item = (&'a Self::Key, &'a mut Self::Value)>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type Keys<'a>: Iterator<Item = &'a Self::Key>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a;
|
|
||||||
type Values<'a>: Iterator<Item = &'a Self::Value>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type ValuesMut<'a>: Iterator<Item = &'a mut Self::Value>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type OccupiedEntry<'a>: OccupiedEntry<'a, Map = Self>
|
|
||||||
where
|
|
||||||
Self: 'a;
|
|
||||||
type VacantEntry<'a>: VacantEntry<'a, Map = Self>
|
|
||||||
where
|
|
||||||
Self: 'a;
|
|
||||||
fn clear(&mut self);
|
|
||||||
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self>;
|
|
||||||
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value>;
|
|
||||||
fn into_keys(self) -> Self::IntoKeys;
|
|
||||||
fn into_values(self) -> Self::IntoValues;
|
|
||||||
fn is_empty(&self) -> bool;
|
|
||||||
fn iter(&self) -> Self::Iter<'_>;
|
|
||||||
fn iter_mut(&mut self) -> Self::IterMut<'_>;
|
|
||||||
fn keys(&self) -> Self::Keys<'_>;
|
|
||||||
fn len(&self) -> usize;
|
|
||||||
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F);
|
|
||||||
fn values(&self) -> Self::Values<'_>;
|
|
||||||
fn values_mut(&mut self) -> Self::ValuesMut<'_>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait MapGet<Q: ?Sized>: Map {
|
|
||||||
fn contains_key(&self, k: &Q) -> bool;
|
|
||||||
fn get(&self, k: &Q) -> Option<&Self::Value>;
|
|
||||||
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value>;
|
|
||||||
fn remove(&mut self, k: &Q) -> Option<Self::Value>;
|
|
||||||
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)>;
|
|
||||||
}
|
|
||||||
|
|
||||||
mod hash_map {
|
|
||||||
use super::*;
|
|
||||||
use crate::util::HashMap;
|
|
||||||
use hashbrown::{Equivalent, hash_map};
|
|
||||||
use std::hash::{BuildHasher, Hash};
|
|
||||||
|
|
||||||
impl<K: Eq + Hash, V, H: BuildHasher + Default> Map for HashMap<K, V, H> {
|
|
||||||
type Key = K;
|
|
||||||
type Value = V;
|
|
||||||
type IntoKeys = hash_map::IntoKeys<K, V>;
|
|
||||||
type IntoValues = hash_map::IntoValues<K, V>;
|
|
||||||
type Iter<'a>
|
|
||||||
= hash_map::Iter<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type IterMut<'a>
|
|
||||||
= hash_map::IterMut<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type Keys<'a>
|
|
||||||
= hash_map::Keys<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a;
|
|
||||||
type Values<'a>
|
|
||||||
= hash_map::Values<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type ValuesMut<'a>
|
|
||||||
= hash_map::ValuesMut<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type OccupiedEntry<'a>
|
|
||||||
= hash_map::OccupiedEntry<'a, K, V, H>
|
|
||||||
where
|
|
||||||
Self: 'a;
|
|
||||||
type VacantEntry<'a>
|
|
||||||
= hash_map::VacantEntry<'a, K, V, H>
|
|
||||||
where
|
|
||||||
Self: 'a;
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.clear();
|
|
||||||
}
|
|
||||||
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self> {
|
|
||||||
use hash_map::Entry::*;
|
|
||||||
match self.entry(k) {
|
|
||||||
Occupied(entry) => Entry::Occupied(entry),
|
|
||||||
Vacant(entry) => Entry::Vacant(entry),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value> {
|
|
||||||
self.insert(k, v)
|
|
||||||
}
|
|
||||||
fn into_keys(self) -> Self::IntoKeys {
|
|
||||||
self.into_keys()
|
|
||||||
}
|
|
||||||
fn into_values(self) -> Self::IntoValues {
|
|
||||||
self.into_values()
|
|
||||||
}
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.is_empty()
|
|
||||||
}
|
|
||||||
fn iter(&self) -> Self::Iter<'_> {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
fn iter_mut(&mut self) -> Self::IterMut<'_> {
|
|
||||||
self.iter_mut()
|
|
||||||
}
|
|
||||||
fn keys(&self) -> Self::Keys<'_> {
|
|
||||||
self.keys()
|
|
||||||
}
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.len()
|
|
||||||
}
|
|
||||||
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F) {
|
|
||||||
self.retain(f);
|
|
||||||
}
|
|
||||||
fn values(&self) -> Self::Values<'_> {
|
|
||||||
self.values()
|
|
||||||
}
|
|
||||||
fn values_mut(&mut self) -> Self::ValuesMut<'_> {
|
|
||||||
self.values_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: Eq + Hash, V, H: BuildHasher + Default, Q: ?Sized + Hash + Equivalent<K>> MapGet<Q>
|
|
||||||
for HashMap<K, V, H>
|
|
||||||
{
|
|
||||||
fn contains_key(&self, k: &Q) -> bool {
|
|
||||||
self.contains_key(k)
|
|
||||||
}
|
|
||||||
fn get(&self, k: &Q) -> Option<&Self::Value> {
|
|
||||||
self.get(k)
|
|
||||||
}
|
|
||||||
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value> {
|
|
||||||
self.get_mut(k)
|
|
||||||
}
|
|
||||||
fn remove(&mut self, k: &Q) -> Option<Self::Value> {
|
|
||||||
self.remove(k)
|
|
||||||
}
|
|
||||||
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)> {
|
|
||||||
self.remove_entry(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K: Eq + Hash, V, H: BuildHasher + Default> VacantEntry<'a>
|
|
||||||
for hash_map::VacantEntry<'a, K, V, H>
|
|
||||||
{
|
|
||||||
type Map = HashMap<K, V, H>;
|
|
||||||
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value {
|
|
||||||
self.insert(v)
|
|
||||||
}
|
|
||||||
fn insert_entry(
|
|
||||||
self,
|
|
||||||
v: <Self::Map as Map>::Value,
|
|
||||||
) -> <Self::Map as Map>::OccupiedEntry<'a> {
|
|
||||||
self.insert_entry(v)
|
|
||||||
}
|
|
||||||
fn into_key(self) -> <Self::Map as Map>::Key {
|
|
||||||
self.into_key()
|
|
||||||
}
|
|
||||||
fn key(&self) -> &<Self::Map as Map>::Key {
|
|
||||||
self.key()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K: Eq + Hash, V, H: BuildHasher + Default> OccupiedEntry<'a>
|
|
||||||
for hash_map::OccupiedEntry<'a, K, V, H>
|
|
||||||
{
|
|
||||||
type Map = HashMap<K, V, H>;
|
|
||||||
fn get(&self) -> &<Self::Map as Map>::Value {
|
|
||||||
self.get()
|
|
||||||
}
|
|
||||||
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value {
|
|
||||||
self.get_mut()
|
|
||||||
}
|
|
||||||
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value {
|
|
||||||
self.insert(v)
|
|
||||||
}
|
|
||||||
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value {
|
|
||||||
self.into_mut()
|
|
||||||
}
|
|
||||||
fn key(&self) -> &<Self::Map as Map>::Key {
|
|
||||||
self.key()
|
|
||||||
}
|
|
||||||
fn remove(self) -> <Self::Map as Map>::Value {
|
|
||||||
self.remove()
|
|
||||||
}
|
|
||||||
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value) {
|
|
||||||
self.remove_entry()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod btree_map {
|
|
||||||
use super::*;
|
|
||||||
use std::collections::{BTreeMap, btree_map};
|
|
||||||
|
|
||||||
impl<K: Ord, V> Map for BTreeMap<K, V> {
|
|
||||||
type Key = K;
|
|
||||||
type Value = V;
|
|
||||||
type IntoKeys = btree_map::IntoKeys<K, V>;
|
|
||||||
type IntoValues = btree_map::IntoValues<K, V>;
|
|
||||||
type Iter<'a>
|
|
||||||
= btree_map::Iter<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type IterMut<'a>
|
|
||||||
= btree_map::IterMut<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type Keys<'a>
|
|
||||||
= btree_map::Keys<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Key: 'a;
|
|
||||||
type Values<'a>
|
|
||||||
= btree_map::Values<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type ValuesMut<'a>
|
|
||||||
= btree_map::ValuesMut<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
Self::Value: 'a;
|
|
||||||
type OccupiedEntry<'a>
|
|
||||||
= btree_map::OccupiedEntry<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a;
|
|
||||||
type VacantEntry<'a>
|
|
||||||
= btree_map::VacantEntry<'a, K, V>
|
|
||||||
where
|
|
||||||
Self: 'a;
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.clear();
|
|
||||||
}
|
|
||||||
fn entry(&mut self, k: Self::Key) -> Entry<'_, Self> {
|
|
||||||
use btree_map::Entry::*;
|
|
||||||
match self.entry(k) {
|
|
||||||
Occupied(entry) => Entry::Occupied(entry),
|
|
||||||
Vacant(entry) => Entry::Vacant(entry),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option<Self::Value> {
|
|
||||||
self.insert(k, v)
|
|
||||||
}
|
|
||||||
fn into_keys(self) -> Self::IntoKeys {
|
|
||||||
self.into_keys()
|
|
||||||
}
|
|
||||||
fn into_values(self) -> Self::IntoValues {
|
|
||||||
self.into_values()
|
|
||||||
}
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.is_empty()
|
|
||||||
}
|
|
||||||
fn iter(&self) -> Self::Iter<'_> {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
fn iter_mut(&mut self) -> Self::IterMut<'_> {
|
|
||||||
self.iter_mut()
|
|
||||||
}
|
|
||||||
fn keys(&self) -> Self::Keys<'_> {
|
|
||||||
self.keys()
|
|
||||||
}
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.len()
|
|
||||||
}
|
|
||||||
fn retain<F: FnMut(&Self::Key, &mut Self::Value) -> bool>(&mut self, f: F) {
|
|
||||||
self.retain(f);
|
|
||||||
}
|
|
||||||
fn values(&self) -> Self::Values<'_> {
|
|
||||||
self.values()
|
|
||||||
}
|
|
||||||
fn values_mut(&mut self) -> Self::ValuesMut<'_> {
|
|
||||||
self.values_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: Ord + std::borrow::Borrow<Q>, V, Q: ?Sized + Ord> MapGet<Q> for BTreeMap<K, V> {
|
|
||||||
fn contains_key(&self, k: &Q) -> bool {
|
|
||||||
self.contains_key(k)
|
|
||||||
}
|
|
||||||
fn get(&self, k: &Q) -> Option<&Self::Value> {
|
|
||||||
self.get(k)
|
|
||||||
}
|
|
||||||
fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value> {
|
|
||||||
self.get_mut(k)
|
|
||||||
}
|
|
||||||
fn remove(&mut self, k: &Q) -> Option<Self::Value> {
|
|
||||||
self.remove(k)
|
|
||||||
}
|
|
||||||
fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)> {
|
|
||||||
self.remove_entry(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K: Ord, V> VacantEntry<'a> for btree_map::VacantEntry<'a, K, V> {
|
|
||||||
type Map = BTreeMap<K, V>;
|
|
||||||
fn insert(self, v: <Self::Map as Map>::Value) -> &'a mut <Self::Map as Map>::Value {
|
|
||||||
self.insert(v)
|
|
||||||
}
|
|
||||||
fn insert_entry(
|
|
||||||
self,
|
|
||||||
v: <Self::Map as Map>::Value,
|
|
||||||
) -> <Self::Map as Map>::OccupiedEntry<'a> {
|
|
||||||
self.insert_entry(v)
|
|
||||||
}
|
|
||||||
fn into_key(self) -> <Self::Map as Map>::Key {
|
|
||||||
self.into_key()
|
|
||||||
}
|
|
||||||
fn key(&self) -> &<Self::Map as Map>::Key {
|
|
||||||
self.key()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K: Ord, V> OccupiedEntry<'a> for btree_map::OccupiedEntry<'a, K, V> {
|
|
||||||
type Map = BTreeMap<K, V>;
|
|
||||||
fn get(&self) -> &<Self::Map as Map>::Value {
|
|
||||||
self.get()
|
|
||||||
}
|
|
||||||
fn get_mut(&mut self) -> &mut <Self::Map as Map>::Value {
|
|
||||||
self.get_mut()
|
|
||||||
}
|
|
||||||
fn insert(&mut self, v: <Self::Map as Map>::Value) -> <Self::Map as Map>::Value {
|
|
||||||
self.insert(v)
|
|
||||||
}
|
|
||||||
fn into_mut(self) -> &'a mut <Self::Map as Map>::Value {
|
|
||||||
self.into_mut()
|
|
||||||
}
|
|
||||||
fn key(&self) -> &<Self::Map as Map>::Key {
|
|
||||||
self.key()
|
|
||||||
}
|
|
||||||
fn remove(self) -> <Self::Map as Map>::Value {
|
|
||||||
self.remove()
|
|
||||||
}
|
|
||||||
fn remove_entry(self) -> (<Self::Map as Map>::Key, <Self::Map as Map>::Value) {
|
|
||||||
self.remove_entry()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,9 +4,7 @@ use crate::intern::{Intern, Interned};
|
||||||
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
|
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
ffi::OsStr,
|
|
||||||
fmt::{self, Debug, Write},
|
fmt::{self, Debug, Write},
|
||||||
io,
|
|
||||||
ops::{Bound, Range, RangeBounds},
|
ops::{Bound, Range, RangeBounds},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, OnceLock},
|
sync::{Arc, OnceLock},
|
||||||
|
|
@ -245,410 +243,3 @@ pub fn try_slice_range<R: RangeBounds<usize>>(range: R, size: usize) -> Option<R
|
||||||
pub fn slice_range<R: RangeBounds<usize>>(range: R, size: usize) -> Range<usize> {
|
pub fn slice_range<R: RangeBounds<usize>>(range: R, size: usize) -> Range<usize> {
|
||||||
try_slice_range(range, size).expect("range out of bounds")
|
try_slice_range(range, size).expect("range out of bounds")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SerdeJsonEscapeIfTest {
|
|
||||||
fn char_needs_escape(&mut self, ch: char) -> serde_json::Result<bool>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait SerdeJsonEscapeIfTestResult {
|
|
||||||
fn to_result(self) -> serde_json::Result<bool>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerdeJsonEscapeIfTestResult for bool {
|
|
||||||
fn to_result(self) -> serde_json::Result<bool> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Into<serde_json::Error>> SerdeJsonEscapeIfTestResult for Result<bool, E> {
|
|
||||||
fn to_result(self) -> serde_json::Result<bool> {
|
|
||||||
self.map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + FnMut(char) -> R, R: SerdeJsonEscapeIfTestResult> SerdeJsonEscapeIfTest for T {
|
|
||||||
fn char_needs_escape(&mut self, ch: char) -> serde_json::Result<bool> {
|
|
||||||
self(ch).to_result()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait SerdeJsonEscapeIfFormatter: serde_json::ser::Formatter {
|
|
||||||
fn write_unicode_escape<W>(&mut self, writer: &mut W, ch: char) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
for utf16 in ch.encode_utf16(&mut [0; 2]) {
|
|
||||||
write!(writer, "\\u{utf16:04x}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerdeJsonEscapeIfFormatter for serde_json::ser::CompactFormatter {}
|
|
||||||
impl SerdeJsonEscapeIfFormatter for serde_json::ser::PrettyFormatter<'_> {}
|
|
||||||
|
|
||||||
pub struct SerdeJsonEscapeIf<Test, Base = serde_json::ser::CompactFormatter> {
|
|
||||||
pub base: Base,
|
|
||||||
pub test: Test,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Test: SerdeJsonEscapeIfTest, Base: SerdeJsonEscapeIfFormatter> serde_json::ser::Formatter
|
|
||||||
for SerdeJsonEscapeIf<Test, Base>
|
|
||||||
{
|
|
||||||
fn write_null<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_null(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_bool<W>(&mut self, writer: &mut W, value: bool) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_bool(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i8<W>(&mut self, writer: &mut W, value: i8) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_i8(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i16<W>(&mut self, writer: &mut W, value: i16) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_i16(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i32<W>(&mut self, writer: &mut W, value: i32) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_i32(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i64<W>(&mut self, writer: &mut W, value: i64) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_i64(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i128<W>(&mut self, writer: &mut W, value: i128) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_i128(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u8<W>(&mut self, writer: &mut W, value: u8) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_u8(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u16<W>(&mut self, writer: &mut W, value: u16) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_u16(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u32<W>(&mut self, writer: &mut W, value: u32) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_u32(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u64<W>(&mut self, writer: &mut W, value: u64) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_u64(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u128<W>(&mut self, writer: &mut W, value: u128) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_u128(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_f32<W>(&mut self, writer: &mut W, value: f32) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_f32(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_f64<W>(&mut self, writer: &mut W, value: f64) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_f64(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_number_str<W>(&mut self, writer: &mut W, value: &str) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_number_str(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn begin_string<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.begin_string(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end_string<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.end_string(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_string_fragment<W>(&mut self, writer: &mut W, mut fragment: &str) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
while let Some((next_escape_index, next_escape_char)) = fragment
|
|
||||||
.char_indices()
|
|
||||||
.find_map(|(index, ch)| match self.test.char_needs_escape(ch) {
|
|
||||||
Ok(false) => None,
|
|
||||||
Ok(true) => Some(Ok((index, ch))),
|
|
||||||
Err(e) => Some(Err(e)),
|
|
||||||
})
|
|
||||||
.transpose()?
|
|
||||||
{
|
|
||||||
let (no_escapes, rest) = fragment.split_at(next_escape_index);
|
|
||||||
fragment = &rest[next_escape_char.len_utf8()..];
|
|
||||||
self.base.write_string_fragment(writer, no_escapes)?;
|
|
||||||
self.base.write_unicode_escape(writer, next_escape_char)?;
|
|
||||||
}
|
|
||||||
self.base.write_string_fragment(writer, fragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_char_escape<W>(
|
|
||||||
&mut self,
|
|
||||||
writer: &mut W,
|
|
||||||
char_escape: serde_json::ser::CharEscape,
|
|
||||||
) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_char_escape(writer, char_escape)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_byte_array<W>(&mut self, writer: &mut W, value: &[u8]) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_byte_array(writer, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn begin_array<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.begin_array(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end_array<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.end_array(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn begin_array_value<W>(&mut self, writer: &mut W, first: bool) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.begin_array_value(writer, first)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end_array_value<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.end_array_value(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn begin_object<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.begin_object(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end_object<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.end_object(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn begin_object_key<W>(&mut self, writer: &mut W, first: bool) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.begin_object_key(writer, first)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end_object_key<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.end_object_key(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn begin_object_value<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.begin_object_value(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end_object_value<W>(&mut self, writer: &mut W) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.end_object_value(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_raw_fragment<W>(&mut self, writer: &mut W, fragment: &str) -> io::Result<()>
|
|
||||||
where
|
|
||||||
W: ?Sized + io::Write,
|
|
||||||
{
|
|
||||||
self.base.write_raw_fragment(writer, fragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_to_json_ascii_helper<F: SerdeJsonEscapeIfFormatter, S: serde::Serialize + ?Sized>(
|
|
||||||
v: &S,
|
|
||||||
base: F,
|
|
||||||
) -> serde_json::Result<String> {
|
|
||||||
let mut retval = Vec::new();
|
|
||||||
v.serialize(&mut serde_json::ser::Serializer::with_formatter(
|
|
||||||
&mut retval,
|
|
||||||
SerdeJsonEscapeIf {
|
|
||||||
base,
|
|
||||||
test: |ch| ch < '\x20' || ch > '\x7F',
|
|
||||||
},
|
|
||||||
))?;
|
|
||||||
String::from_utf8(retval).map_err(|_| serde::ser::Error::custom("invalid UTF-8"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serialize_to_json_ascii<T: serde::Serialize + ?Sized>(v: &T) -> serde_json::Result<String> {
|
|
||||||
serialize_to_json_ascii_helper(v, serde_json::ser::CompactFormatter)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serialize_to_json_ascii_pretty<T: serde::Serialize + ?Sized>(
|
|
||||||
v: &T,
|
|
||||||
) -> serde_json::Result<String> {
|
|
||||||
serialize_to_json_ascii_helper(v, serde_json::ser::PrettyFormatter::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serialize_to_json_ascii_pretty_with_indent<T: serde::Serialize + ?Sized>(
|
|
||||||
v: &T,
|
|
||||||
indent: &str,
|
|
||||||
) -> serde_json::Result<String> {
|
|
||||||
serialize_to_json_ascii_helper(
|
|
||||||
v,
|
|
||||||
serde_json::ser::PrettyFormatter::with_indent(indent.as_bytes()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn os_str_strip_prefix<'a>(os_str: &'a OsStr, prefix: impl AsRef<str>) -> Option<&'a OsStr> {
|
|
||||||
os_str
|
|
||||||
.as_encoded_bytes()
|
|
||||||
.strip_prefix(prefix.as_ref().as_bytes())
|
|
||||||
.map(|bytes| {
|
|
||||||
// Safety: we removed a UTF-8 prefix so bytes starts with a valid boundary
|
|
||||||
unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn os_str_strip_suffix<'a>(os_str: &'a OsStr, suffix: impl AsRef<str>) -> Option<&'a OsStr> {
|
|
||||||
os_str
|
|
||||||
.as_encoded_bytes()
|
|
||||||
.strip_suffix(suffix.as_ref().as_bytes())
|
|
||||||
.map(|bytes| {
|
|
||||||
// Safety: we removed a UTF-8 suffix so bytes ends with a valid boundary
|
|
||||||
unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub(crate) struct InternedStrCompareAsStr(pub(crate) Interned<str>);
|
|
||||||
|
|
||||||
impl fmt::Debug for InternedStrCompareAsStr {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
self.0.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for InternedStrCompareAsStr {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
str::cmp(&self.0, &other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for InternedStrCompareAsStr {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::borrow::Borrow<str> for InternedStrCompareAsStr {
|
|
||||||
fn borrow(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn copy_le_bytes_to_bitslice(
|
|
||||||
dest: &mut BitSlice<usize, Lsb0>,
|
|
||||||
bytes: &[u8],
|
|
||||||
msb_fill: bool,
|
|
||||||
) {
|
|
||||||
let (chunks, remainder) = bytes.as_chunks();
|
|
||||||
let mut filled_to = 0;
|
|
||||||
for (i, chunk) in chunks.iter().enumerate() {
|
|
||||||
if let Some(start_bit_index) = i.checked_mul(usize::BITS as usize)
|
|
||||||
&& start_bit_index < dest.len()
|
|
||||||
{
|
|
||||||
let end_bit_index = start_bit_index
|
|
||||||
.saturating_add(usize::BITS as usize)
|
|
||||||
.min(dest.len());
|
|
||||||
let bit_len = end_bit_index - start_bit_index;
|
|
||||||
let chunk = usize::from_le_bytes(*chunk);
|
|
||||||
dest[start_bit_index..end_bit_index].copy_from_bitslice(&chunk.view_bits()[..bit_len]);
|
|
||||||
filled_to = end_bit_index;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !remainder.is_empty() {
|
|
||||||
if let Some(start_bit_index) = chunks.len().checked_mul(usize::BITS as usize)
|
|
||||||
&& start_bit_index < dest.len()
|
|
||||||
{
|
|
||||||
let end_bit_index = start_bit_index
|
|
||||||
.saturating_add(usize::BITS as usize)
|
|
||||||
.min(dest.len());
|
|
||||||
let bit_len = end_bit_index - start_bit_index;
|
|
||||||
let mut chunk = [if msb_fill { !0 } else { 0 }; _];
|
|
||||||
chunk[..remainder.len()].copy_from_slice(remainder);
|
|
||||||
let chunk = usize::from_le_bytes(chunk);
|
|
||||||
dest[start_bit_index..end_bit_index].copy_from_bitslice(&chunk.view_bits()[..bit_len]);
|
|
||||||
filled_to = end_bit_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dest[filled_to..].fill(msb_fill);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ impl<T: Type> ReadyValid<T> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub fn firing_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> {
|
pub fn firing_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> {
|
||||||
let expr = expr.to_expr();
|
let expr = expr.to_expr();
|
||||||
let option_ty = expr.ty().data;
|
let option_ty = Expr::ty(expr).data;
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let firing_data = wire(option_ty);
|
let firing_data = wire(option_ty);
|
||||||
connect(firing_data, option_ty.HdlNone());
|
connect(firing_data, option_ty.HdlNone());
|
||||||
|
|
@ -42,7 +42,7 @@ impl<T: Type> ReadyValid<T> {
|
||||||
) -> Expr<ReadyValid<R>> {
|
) -> Expr<ReadyValid<R>> {
|
||||||
let data = HdlOption::map(expr.data, f);
|
let data = HdlOption::map(expr.data, f);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let mapped = wire(ReadyValid[data.ty().HdlSome]);
|
let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]);
|
||||||
connect(mapped.data, data);
|
connect(mapped.data, data);
|
||||||
connect(expr.ready, mapped.ready);
|
connect(expr.ready, mapped.ready);
|
||||||
mapped
|
mapped
|
||||||
|
|
@ -81,7 +81,7 @@ pub fn queue<T: Type>(
|
||||||
let count: UInt = m.output(count_ty);
|
let count: UInt = m.output(count_ty);
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let inp_index_reg: UInt = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -212,7 +212,9 @@ pub fn queue<T: Type>(
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
firrtl::ExportOptions, module::transform::simplify_enums::SimplifyEnumsKind, ty::StaticType,
|
cli::FormalMode, firrtl::ExportOptions,
|
||||||
|
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
|
||||||
|
ty::StaticType,
|
||||||
};
|
};
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
|
|
||||||
|
|
@ -241,13 +243,15 @@ mod tests {
|
||||||
/// happens to be in phase with the offending input or output).
|
/// happens to be in phase with the offending input or output).
|
||||||
#[hdl_module]
|
#[hdl_module]
|
||||||
fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
|
fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
|
||||||
|
#[hdl]
|
||||||
|
let clk: Clock = m.input();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let cd = wire();
|
let cd = wire();
|
||||||
connect(
|
connect(
|
||||||
cd,
|
cd,
|
||||||
#[hdl]
|
#[hdl]
|
||||||
ClockDomain {
|
ClockDomain {
|
||||||
clk: formal_global_clock(),
|
clk,
|
||||||
rst: formal_reset().to_reset(),
|
rst: formal_reset().to_reset(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -278,7 +282,7 @@ mod tests {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let index_to_check = wire(index_ty);
|
let index_to_check = wire(index_ty);
|
||||||
connect(index_to_check, any_const(index_ty));
|
connect(index_to_check, any_const(index_ty));
|
||||||
hdl_assume(cd.clk, index_to_check.cmp_lt(capacity.get()), "");
|
hdl_assume(clk, index_to_check.cmp_lt(capacity.get()), "");
|
||||||
|
|
||||||
// instantiate and connect the queue
|
// instantiate and connect the queue
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -298,13 +302,13 @@ mod tests {
|
||||||
let expected_count_reg = reg_builder().clock_domain(cd).reset(count_ty.zero());
|
let expected_count_reg = reg_builder().clock_domain(cd).reset(count_ty.zero());
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) {
|
if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) {
|
||||||
hdl_assert(cd.clk, expected_count_reg.cmp_ne(capacity.get()), "");
|
hdl_assert(clk, expected_count_reg.cmp_ne(capacity.get()), "");
|
||||||
connect_any(expected_count_reg, expected_count_reg + 1u8);
|
connect_any(expected_count_reg, expected_count_reg + 1u8);
|
||||||
} else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) {
|
} else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) {
|
||||||
hdl_assert(cd.clk, expected_count_reg.cmp_ne(count_ty.zero()), "");
|
hdl_assert(clk, expected_count_reg.cmp_ne(count_ty.zero()), "");
|
||||||
connect_any(expected_count_reg, expected_count_reg - 1u8);
|
connect_any(expected_count_reg, expected_count_reg - 1u8);
|
||||||
}
|
}
|
||||||
hdl_assert(cd.clk, expected_count_reg.cmp_eq(dut.count), "");
|
hdl_assert(clk, expected_count_reg.cmp_eq(dut.count), "");
|
||||||
|
|
||||||
// keep an independent write index into the FIFO's circular buffer
|
// keep an independent write index into the FIFO's circular buffer
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -372,7 +376,7 @@ mod tests {
|
||||||
match inp_firing_data {
|
match inp_firing_data {
|
||||||
// ... and we are not receiving data, then we must not
|
// ... and we are not receiving data, then we must not
|
||||||
// transmit any data.
|
// transmit any data.
|
||||||
HdlNone => hdl_assert(cd.clk, HdlOption::is_none(out_firing_data), ""),
|
HdlNone => hdl_assert(clk, HdlOption::is_none(out_firing_data), ""),
|
||||||
// If we are indeed receiving some data...
|
// If we are indeed receiving some data...
|
||||||
HdlSome(data_in) => {
|
HdlSome(data_in) => {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -380,9 +384,7 @@ mod tests {
|
||||||
// ... and transmitting at the same time, we
|
// ... and transmitting at the same time, we
|
||||||
// must be transmitting the input data itself,
|
// must be transmitting the input data itself,
|
||||||
// since the holding register is empty.
|
// since the holding register is empty.
|
||||||
HdlSome(data_out) => {
|
HdlSome(data_out) => hdl_assert(clk, data_out.cmp_eq(data_in), ""),
|
||||||
hdl_assert(cd.clk, data_out.cmp_eq(data_in), "")
|
|
||||||
}
|
|
||||||
// If we are receiving, but not transmitting,
|
// If we are receiving, but not transmitting,
|
||||||
// store the received data in the holding
|
// store the received data in the holding
|
||||||
// register.
|
// register.
|
||||||
|
|
@ -397,11 +399,11 @@ mod tests {
|
||||||
match out_firing_data {
|
match out_firing_data {
|
||||||
// ... and we are not transmitting it, we cannot
|
// ... and we are not transmitting it, we cannot
|
||||||
// receive any more data.
|
// receive any more data.
|
||||||
HdlNone => hdl_assert(cd.clk, HdlOption::is_none(inp_firing_data), ""),
|
HdlNone => hdl_assert(clk, HdlOption::is_none(inp_firing_data), ""),
|
||||||
// If we are transmitting a previously stored value...
|
// If we are transmitting a previously stored value...
|
||||||
HdlSome(data_out) => {
|
HdlSome(data_out) => {
|
||||||
// ... it must be the same data we stored earlier.
|
// ... it must be the same data we stored earlier.
|
||||||
hdl_assert(cd.clk, data_out.cmp_eq(stored), "");
|
hdl_assert(clk, data_out.cmp_eq(stored), "");
|
||||||
// Also, accept new data, if any. Otherwise,
|
// Also, accept new data, if any. Otherwise,
|
||||||
// let the holding register become empty.
|
// let the holding register become empty.
|
||||||
connect(stored_reg, inp_firing_data);
|
connect(stored_reg, inp_firing_data);
|
||||||
|
|
@ -417,17 +419,17 @@ mod tests {
|
||||||
connect(dut.dbg.index_to_check, index_to_check);
|
connect(dut.dbg.index_to_check, index_to_check);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
if let HdlSome(stored) = stored_reg {
|
if let HdlSome(stored) = stored_reg {
|
||||||
hdl_assert(cd.clk, stored.cmp_eq(dut.dbg.stored), "");
|
hdl_assert(clk, stored.cmp_eq(dut.dbg.stored), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync the read and write indices
|
// sync the read and write indices
|
||||||
hdl_assert(cd.clk, inp_index_reg.cmp_eq(dut.dbg.inp_index), "");
|
hdl_assert(clk, inp_index_reg.cmp_eq(dut.dbg.inp_index), "");
|
||||||
hdl_assert(cd.clk, out_index_reg.cmp_eq(dut.dbg.out_index), "");
|
hdl_assert(clk, out_index_reg.cmp_eq(dut.dbg.out_index), "");
|
||||||
|
|
||||||
// the indices should never go past the capacity, but induction
|
// the indices should never go past the capacity, but induction
|
||||||
// doesn't know that...
|
// doesn't know that...
|
||||||
hdl_assert(cd.clk, inp_index_reg.cmp_lt(capacity.get()), "");
|
hdl_assert(clk, inp_index_reg.cmp_lt(capacity.get()), "");
|
||||||
hdl_assert(cd.clk, out_index_reg.cmp_lt(capacity.get()), "");
|
hdl_assert(clk, out_index_reg.cmp_lt(capacity.get()), "");
|
||||||
|
|
||||||
// strongly constrain the state of the holding register
|
// strongly constrain the state of the holding register
|
||||||
//
|
//
|
||||||
|
|
@ -455,7 +457,7 @@ mod tests {
|
||||||
connect(expected_stored, pending_reads.cmp_lt(dut.count));
|
connect(expected_stored, pending_reads.cmp_lt(dut.count));
|
||||||
// sync with the state of the holding register
|
// sync with the state of the holding register
|
||||||
hdl_assert(
|
hdl_assert(
|
||||||
cd.clk,
|
clk,
|
||||||
expected_stored.cmp_eq(HdlOption::is_some(stored_reg)),
|
expected_stored.cmp_eq(HdlOption::is_some(stored_reg)),
|
||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::util::HashMap;
|
|
||||||
use hashbrown::hash_map::Entry;
|
|
||||||
use serde::{Deserialize, Serialize, de::Error};
|
|
||||||
use std::{
|
|
||||||
any::TypeId,
|
|
||||||
borrow::Cow,
|
|
||||||
fmt::Write,
|
|
||||||
hash::{BuildHasher, Hash, Hasher},
|
|
||||||
marker::PhantomData,
|
|
||||||
sync::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) struct SerdeByIdProperties<T: SerdeByIdTrait> {
|
|
||||||
type_id: TypeId,
|
|
||||||
type_name: &'static str,
|
|
||||||
_phantom: PhantomData<fn(T) -> T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> Clone for SerdeByIdProperties<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> Copy for SerdeByIdProperties<T> {}
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> SerdeByIdProperties<T> {
|
|
||||||
pub fn of<U: ?Sized + 'static>() -> Self {
|
|
||||||
Self {
|
|
||||||
type_id: TypeId::of::<U>(),
|
|
||||||
type_name: std::any::type_name::<U>(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) trait SerdeByIdTrait: Hash + Eq + Clone + 'static + Send {
|
|
||||||
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Self>;
|
|
||||||
fn static_table() -> &'static SerdeByIdTable<Self>;
|
|
||||||
const NAME: &'static str;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(transparent)]
|
|
||||||
struct SerdeRandomId([u32; 4]);
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub(crate) struct SerdeId<'a, T: SerdeByIdTrait> {
|
|
||||||
random_id: SerdeRandomId,
|
|
||||||
#[serde(borrow)]
|
|
||||||
type_name: Cow<'a, str>,
|
|
||||||
#[serde(skip)]
|
|
||||||
_phantom: PhantomData<fn(T) -> T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: SerdeByIdTrait> Clone for SerdeId<'a, T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
random_id: self.random_id,
|
|
||||||
type_name: self.type_name.clone(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: SerdeByIdTrait> Eq for SerdeId<'a, T> {}
|
|
||||||
|
|
||||||
impl<'a, 'b, T: SerdeByIdTrait> PartialEq<SerdeId<'b, T>> for SerdeId<'a, T> {
|
|
||||||
fn eq(&self, other: &SerdeId<'b, T>) -> bool {
|
|
||||||
let Self {
|
|
||||||
random_id,
|
|
||||||
type_name,
|
|
||||||
_phantom: _,
|
|
||||||
} = self;
|
|
||||||
*random_id == other.random_id && *type_name == other.type_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: SerdeByIdTrait> Hash for SerdeId<'a, T> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
let Self {
|
|
||||||
random_id,
|
|
||||||
type_name: _,
|
|
||||||
_phantom: _,
|
|
||||||
} = self;
|
|
||||||
random_id.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SerdeByIdTableRest<T: SerdeByIdTrait> {
|
|
||||||
from_serde: HashMap<SerdeId<'static, T>, T>,
|
|
||||||
serde_id_random_state: std::hash::RandomState,
|
|
||||||
buffer: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> Default for SerdeByIdTableRest<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
from_serde: Default::default(),
|
|
||||||
serde_id_random_state: Default::default(),
|
|
||||||
buffer: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> SerdeByIdTableRest<T> {
|
|
||||||
fn add_new(&mut self, value: T) -> SerdeId<'static, T> {
|
|
||||||
let properties = value.serde_by_id_properties();
|
|
||||||
let mut try_number = 0u64;
|
|
||||||
let mut hasher = self.serde_id_random_state.build_hasher();
|
|
||||||
// extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits
|
|
||||||
write!(self.buffer, "{:?}", properties.type_id).expect("shouldn't ever fail");
|
|
||||||
self.buffer.hash(&mut hasher);
|
|
||||||
loop {
|
|
||||||
let mut hasher = hasher.clone();
|
|
||||||
try_number.hash(&mut hasher);
|
|
||||||
try_number += 1;
|
|
||||||
let key = SerdeId {
|
|
||||||
random_id: SerdeRandomId(std::array::from_fn(|i| {
|
|
||||||
let mut hasher = hasher.clone();
|
|
||||||
i.hash(&mut hasher);
|
|
||||||
hasher.finish() as u32
|
|
||||||
})),
|
|
||||||
type_name: Cow::Borrowed(properties.type_name),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
};
|
|
||||||
match self.from_serde.entry(key) {
|
|
||||||
Entry::Occupied(_) => continue,
|
|
||||||
Entry::Vacant(e) => {
|
|
||||||
let key = e.key().clone();
|
|
||||||
e.insert(value);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct SerdeByIdTableMut<T: SerdeByIdTrait> {
|
|
||||||
to_serde: HashMap<T, SerdeId<'static, T>>,
|
|
||||||
rest: SerdeByIdTableRest<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> Default for SerdeByIdTableMut<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
to_serde: Default::default(),
|
|
||||||
rest: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> SerdeByIdTableMut<T> {
|
|
||||||
pub(crate) fn to_serde(&mut self, value: &T) -> SerdeId<'static, T> {
|
|
||||||
if let Some(retval) = self.to_serde.get(value) {
|
|
||||||
return retval.clone();
|
|
||||||
}
|
|
||||||
self.to_serde_insert(value)
|
|
||||||
}
|
|
||||||
#[cold]
|
|
||||||
fn to_serde_insert(&mut self, value: &T) -> SerdeId<'static, T> {
|
|
||||||
let value = value.clone();
|
|
||||||
let retval = self.rest.add_new(value.clone());
|
|
||||||
self.to_serde.insert(value, retval.clone());
|
|
||||||
retval
|
|
||||||
}
|
|
||||||
pub(crate) fn from_serde(&self, id: &SerdeId<'_, T>) -> Option<T> {
|
|
||||||
self.rest.from_serde.get(id).cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct SerdeByIdTable<T: SerdeByIdTrait>(Mutex<Option<SerdeByIdTableMut<T>>>);
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> SerdeByIdTable<T> {
|
|
||||||
pub(crate) const fn new() -> Self {
|
|
||||||
Self(Mutex::new(None))
|
|
||||||
}
|
|
||||||
pub(crate) fn to_serde(&self, value: &T) -> SerdeId<'static, T> {
|
|
||||||
self.0
|
|
||||||
.lock()
|
|
||||||
.expect("shouldn't be poison")
|
|
||||||
.get_or_insert_with(
|
|
||||||
#[cold]
|
|
||||||
|| Default::default(),
|
|
||||||
)
|
|
||||||
.to_serde(value)
|
|
||||||
}
|
|
||||||
pub(crate) fn from_serde(&self, id: &SerdeId<'_, T>) -> Option<T> {
|
|
||||||
self.0
|
|
||||||
.lock()
|
|
||||||
.expect("shouldn't be poison")
|
|
||||||
.get_or_insert_with(
|
|
||||||
#[cold]
|
|
||||||
|| Default::default(),
|
|
||||||
)
|
|
||||||
.from_serde(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, Ord, PartialOrd)]
|
|
||||||
pub(crate) struct SerdeById<T: SerdeByIdTrait> {
|
|
||||||
pub(crate) inner: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, T: SerdeByIdTrait> Deserialize<'de> for SerdeById<T> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let id = SerdeId::deserialize(deserializer)?;
|
|
||||||
let inner = T::static_table().from_serde(&id).ok_or_else(|| {
|
|
||||||
D::Error::custom(format_args!(
|
|
||||||
"doesn't match any {} that was serialized this time this program was run: type_name={:?}",
|
|
||||||
T::NAME,
|
|
||||||
id.type_name,
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
Ok(Self { inner })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SerdeByIdTrait> Serialize for SerdeById<T> {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
T::static_table()
|
|
||||||
.to_serde(&self.inner)
|
|
||||||
.serialize(serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,352 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::util::{
|
|
||||||
HashMap,
|
|
||||||
map_trait::{self, Map, MapGet, OccupiedEntry as _, VacantEntry as _},
|
|
||||||
};
|
|
||||||
use petgraph::unionfind::UnionFind;
|
|
||||||
use std::{collections::BTreeMap, fmt, marker::PhantomData};
|
|
||||||
|
|
||||||
pub struct UnionFindMap<K, V, M = HashMap<K, usize>> {
|
|
||||||
uf: UnionFind<usize>,
|
|
||||||
keys_to_indexes: M,
|
|
||||||
values: Vec<Option<V>>,
|
|
||||||
_phantom: PhantomData<K>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: fmt::Debug, V: fmt::Debug, M: Map<Key = K, Value = usize>> fmt::Debug
|
|
||||||
for UnionFindMap<K, V, M>
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let mut indexes_to_keys = vec![None; self.len()];
|
|
||||||
for (k, &index) in self.keys_to_indexes.iter() {
|
|
||||||
indexes_to_keys[index] = Some(k);
|
|
||||||
}
|
|
||||||
let mut debug_map = f.debug_map();
|
|
||||||
for (index, key) in indexes_to_keys.into_iter().enumerate() {
|
|
||||||
if let Some(key) = key {
|
|
||||||
debug_map.key(key);
|
|
||||||
} else {
|
|
||||||
debug_map.key(&fmt::from_fn(|f| {
|
|
||||||
f.write_str("<<there's a misbehaving key>>")
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
let set_index = self.uf.find(index);
|
|
||||||
debug_map.value(&fmt::from_fn(|f| {
|
|
||||||
write!(f, "@{set_index} ")?;
|
|
||||||
if set_index == index {
|
|
||||||
let Some(value) = &self.values[index] else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
value.fmt(f)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
debug_map.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V, M: Map<Key = K, Value = usize>> UnionFindMap<K, V, M> {
|
|
||||||
/// returns the number of keys, not the number of sets/values
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.values.len()
|
|
||||||
}
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.len() == 0
|
|
||||||
}
|
|
||||||
pub fn capacity(&self) -> usize {
|
|
||||||
self.values.capacity()
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn equiv<K1: ?Sized, K2: ?Sized>(&self, k1: &K1, k2: &K2) -> bool
|
|
||||||
where
|
|
||||||
M: MapGet<K1> + MapGet<K2>,
|
|
||||||
{
|
|
||||||
self.try_equiv(k1, k2).expect("key not found")
|
|
||||||
}
|
|
||||||
pub fn try_equiv<K1: ?Sized, K2: ?Sized>(&self, k1: &K1, k2: &K2) -> Option<bool>
|
|
||||||
where
|
|
||||||
M: MapGet<K1> + MapGet<K2>,
|
|
||||||
{
|
|
||||||
let &index1 = self.keys_to_indexes.get(k1)?;
|
|
||||||
let &index2 = self.keys_to_indexes.get(k2)?;
|
|
||||||
Some(self.uf.equiv(index1, index2))
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn find<Q: ?Sized>(&self, k: &Q) -> &V
|
|
||||||
where
|
|
||||||
M: MapGet<Q>,
|
|
||||||
{
|
|
||||||
self.try_find(k).expect("key not found")
|
|
||||||
}
|
|
||||||
pub fn try_find<Q: ?Sized>(&self, k: &Q) -> Option<&V>
|
|
||||||
where
|
|
||||||
M: MapGet<Q>,
|
|
||||||
{
|
|
||||||
let &index = self.keys_to_indexes.get(k)?;
|
|
||||||
self.values[self.uf.find(index)].as_ref()
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn find_mut<Q: ?Sized>(&mut self, k: &Q) -> &mut V
|
|
||||||
where
|
|
||||||
M: MapGet<Q>,
|
|
||||||
{
|
|
||||||
self.try_find_mut(k).expect("key not found")
|
|
||||||
}
|
|
||||||
pub fn try_find_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
|
|
||||||
where
|
|
||||||
M: MapGet<Q>,
|
|
||||||
{
|
|
||||||
let &index = self.keys_to_indexes.get(k)?;
|
|
||||||
self.values[self.uf.find_mut(index)].as_mut()
|
|
||||||
}
|
|
||||||
/// inserts a new key as a new set, otherwise replaces the value for the set containing the passed-in key
|
|
||||||
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
|
||||||
match self.entry(k) {
|
|
||||||
Entry::Vacant(entry) => {
|
|
||||||
entry.insert(v);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Entry::Occupied(mut entry) => Some(entry.insert(v)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn entry(&mut self, k: K) -> Entry<'_, K, V, M> {
|
|
||||||
match self.keys_to_indexes.entry(k) {
|
|
||||||
map_trait::Entry::Vacant(keys_to_indexes_entry) => Entry::Vacant(VacantEntry {
|
|
||||||
keys_to_indexes_entry,
|
|
||||||
uf: &mut self.uf,
|
|
||||||
values: &mut self.values,
|
|
||||||
}),
|
|
||||||
map_trait::Entry::Occupied(keys_to_indexes_entry) => {
|
|
||||||
let set_index = self.uf.find_mut(*keys_to_indexes_entry.get());
|
|
||||||
Entry::Occupied(OccupiedEntry {
|
|
||||||
keys_to_indexes_entry,
|
|
||||||
set_index,
|
|
||||||
values: &mut self.values,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Unify the two sets containing `k1` and `k2`.
|
|
||||||
/// If the sets were the same, returns `Some((false, value))`,
|
|
||||||
/// otherwise calling `merge` to merge their values and returning `Some((true, value))`.
|
|
||||||
/// Returns `None` if either of the keys weren't found.
|
|
||||||
pub fn try_union<K1: ?Sized, K2: ?Sized, F>(
|
|
||||||
&mut self,
|
|
||||||
k1: &K1,
|
|
||||||
k2: &K2,
|
|
||||||
merge: F,
|
|
||||||
) -> Option<(bool, &mut V)>
|
|
||||||
where
|
|
||||||
M: MapGet<K1> + MapGet<K2>,
|
|
||||||
F: FnOnce(&K1, V, &K2, V) -> V,
|
|
||||||
{
|
|
||||||
let &index1 = self.keys_to_indexes.get(k1)?;
|
|
||||||
let &index2 = self.keys_to_indexes.get(k2)?;
|
|
||||||
let index1 = self.uf.find_mut(index1);
|
|
||||||
let index2 = self.uf.find_mut(index2);
|
|
||||||
if index1 == index2 {
|
|
||||||
return Some((false, self.values[index1].as_mut()?));
|
|
||||||
}
|
|
||||||
assert!(self.uf.union(index1, index2));
|
|
||||||
let v1 = self.values[index1].take().expect("known to be Some");
|
|
||||||
let v2 = self.values[index2].take().expect("known to be Some");
|
|
||||||
let dest = &mut self.values[self.uf.find_mut(index1)];
|
|
||||||
let dest = dest.insert(merge(k1, v1, k2, v2));
|
|
||||||
Some((true, dest))
|
|
||||||
}
|
|
||||||
/// Unify the two sets containing `k1` and `k2`.
|
|
||||||
/// If the sets were the same, returns `(false, value)`,
|
|
||||||
/// otherwise calling `merge` to merge their values and returning `(true, value)`.
|
|
||||||
/// panics if either of the keys weren't found.
|
|
||||||
#[track_caller]
|
|
||||||
pub fn union<K1: ?Sized, K2: ?Sized, F>(&mut self, k1: &K1, k2: &K2, merge: F) -> (bool, &mut V)
|
|
||||||
where
|
|
||||||
M: MapGet<K1> + MapGet<K2>,
|
|
||||||
F: FnOnce(&K1, V, &K2, V) -> V,
|
|
||||||
{
|
|
||||||
self.try_union(k1, k2, merge).expect("key not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> UnionFindMap<K, V> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::with_hasher(Default::default())
|
|
||||||
}
|
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
|
||||||
Self::with_capacity_and_hasher(capacity, Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> UnionFindMap<K, V, BTreeMap<K, usize>> {
|
|
||||||
pub const fn new_btree() -> Self {
|
|
||||||
Self {
|
|
||||||
uf: UnionFind::new_empty(),
|
|
||||||
keys_to_indexes: BTreeMap::new(),
|
|
||||||
values: Vec::new(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V, H> UnionFindMap<K, V, HashMap<K, usize, H>> {
|
|
||||||
pub const fn with_hasher(hash_builder: H) -> Self {
|
|
||||||
Self {
|
|
||||||
uf: UnionFind::new_empty(),
|
|
||||||
keys_to_indexes: HashMap::with_hasher(hash_builder),
|
|
||||||
values: Vec::new(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: H) -> Self {
|
|
||||||
Self {
|
|
||||||
uf: UnionFind::with_capacity(capacity),
|
|
||||||
keys_to_indexes: HashMap::with_capacity_and_hasher(capacity, hash_builder),
|
|
||||||
values: Vec::with_capacity(capacity),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V, M: Default> Default for UnionFindMap<K, V, M> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
uf: UnionFind::new_empty(),
|
|
||||||
keys_to_indexes: M::default(),
|
|
||||||
values: Vec::new(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OccupiedEntry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
|
|
||||||
keys_to_indexes_entry: M::OccupiedEntry<'a>,
|
|
||||||
set_index: usize,
|
|
||||||
values: &'a mut [Option<V>],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> OccupiedEntry<'a, K, V, M> {
|
|
||||||
pub fn get(&self) -> &V {
|
|
||||||
let Some(v) = &self.values[self.set_index] else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
v
|
|
||||||
}
|
|
||||||
pub fn get_mut(&mut self) -> &mut V {
|
|
||||||
let Some(v) = &mut self.values[self.set_index] else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
v
|
|
||||||
}
|
|
||||||
/// replaces the value for this set
|
|
||||||
pub fn insert(&mut self, v: V) -> V {
|
|
||||||
std::mem::replace(self.get_mut(), v)
|
|
||||||
}
|
|
||||||
pub fn into_mut(self) -> &'a mut V {
|
|
||||||
let Some(v) = &mut self.values[self.set_index] else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
v
|
|
||||||
}
|
|
||||||
pub fn key(&self) -> &K {
|
|
||||||
self.keys_to_indexes_entry.key()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VacantEntry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
|
|
||||||
keys_to_indexes_entry: M::VacantEntry<'a>,
|
|
||||||
uf: &'a mut UnionFind<usize>,
|
|
||||||
values: &'a mut Vec<Option<V>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> VacantEntry<'a, K, V, M> {
|
|
||||||
/// inserts a new key as a new set
|
|
||||||
pub fn insert(self, v: V) -> &'a mut V {
|
|
||||||
self.insert_entry(v).into_mut()
|
|
||||||
}
|
|
||||||
/// inserts a new key as a new set
|
|
||||||
pub fn insert_entry(self, v: V) -> OccupiedEntry<'a, K, V, M> {
|
|
||||||
let Self {
|
|
||||||
keys_to_indexes_entry,
|
|
||||||
uf,
|
|
||||||
values,
|
|
||||||
} = self;
|
|
||||||
let set_index = uf.new_set();
|
|
||||||
values.push(Some(v));
|
|
||||||
OccupiedEntry {
|
|
||||||
keys_to_indexes_entry: keys_to_indexes_entry.insert_entry(set_index),
|
|
||||||
set_index,
|
|
||||||
values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn into_key(self) -> K {
|
|
||||||
self.keys_to_indexes_entry.into_key()
|
|
||||||
}
|
|
||||||
pub fn key(&self) -> &K {
|
|
||||||
self.keys_to_indexes_entry.key()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Entry<'a, K, V, M: Map<Key = K, Value = usize> + 'a> {
|
|
||||||
Vacant(VacantEntry<'a, K, V, M>),
|
|
||||||
Occupied(OccupiedEntry<'a, K, V, M>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K, V, M: Map<Key = K, Value = usize> + 'a> Entry<'a, K, V, M> {
|
|
||||||
pub fn and_modify<F: FnOnce(&mut V)>(mut self, f: F) -> Self {
|
|
||||||
if let Self::Occupied(entry) = &mut self {
|
|
||||||
f(entry.get_mut());
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
/// inserts a new key as a new set, otherwise replaces the value for the set containing the passed-in key
|
|
||||||
pub fn insert_entry(self, v: V) -> OccupiedEntry<'a, K, V, M> {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => entry.insert_entry(v),
|
|
||||||
Self::Occupied(mut entry) => {
|
|
||||||
entry.insert(v);
|
|
||||||
entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn key(&self) -> &K {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => entry.key(),
|
|
||||||
Self::Occupied(entry) => entry.key(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// inserts a new key as a new set
|
|
||||||
pub fn or_default(self) -> &'a mut V
|
|
||||||
where
|
|
||||||
V: Default,
|
|
||||||
{
|
|
||||||
self.or_insert_with(V::default)
|
|
||||||
}
|
|
||||||
/// inserts a new key as a new set
|
|
||||||
pub fn or_insert(self, v: V) -> &'a mut V {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => entry.insert(v),
|
|
||||||
Self::Occupied(entry) => entry.into_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// inserts a new key as a new set
|
|
||||||
pub fn or_insert_with<F: FnOnce() -> V>(self, f: F) -> &'a mut V {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => entry.insert(f()),
|
|
||||||
Self::Occupied(entry) => entry.into_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// inserts a new key as a new set
|
|
||||||
pub fn or_insert_with_key<F: FnOnce(&K) -> V>(self, f: F) -> &'a mut V {
|
|
||||||
match self {
|
|
||||||
Self::Vacant(entry) => {
|
|
||||||
let v = f(entry.key());
|
|
||||||
entry.insert(v)
|
|
||||||
}
|
|
||||||
Self::Occupied(entry) => entry.into_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
pub mod xilinx;
|
|
||||||
|
|
||||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
|
|
||||||
xilinx::built_in_job_kinds()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> {
|
|
||||||
xilinx::built_in_platforms()
|
|
||||||
}
|
|
||||||
207
crates/fayalite/src/vendor/xilinx.rs
vendored
207
crates/fayalite/src/vendor/xilinx.rs
vendored
|
|
@ -1,207 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
annotations::make_annotation_enum,
|
|
||||||
build::{GlobalParams, ToArgs, WriteArgs},
|
|
||||||
intern::Interned,
|
|
||||||
prelude::{DynPlatform, Platform},
|
|
||||||
};
|
|
||||||
use clap::ValueEnum;
|
|
||||||
use ordered_float::NotNan;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
pub mod arty_a7;
|
|
||||||
pub mod primitives;
|
|
||||||
pub mod yosys_nextpnr_prjxray;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub struct XdcIOStandardAnnotation {
|
|
||||||
pub value: Interned<str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub struct XdcLocationAnnotation {
|
|
||||||
pub location: Interned<str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub struct XdcCreateClockAnnotation {
|
|
||||||
/// clock period in nanoseconds
|
|
||||||
pub period: NotNan<f64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
make_annotation_enum! {
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum XilinxAnnotation {
|
|
||||||
XdcIOStandard(XdcIOStandardAnnotation),
|
|
||||||
XdcLocation(XdcLocationAnnotation),
|
|
||||||
XdcCreateClock(XdcCreateClockAnnotation),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
|
|
||||||
pub struct XilinxArgs {
|
|
||||||
#[arg(long)]
|
|
||||||
pub device: Option<Device>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XilinxArgs {
|
|
||||||
pub fn require_device(
|
|
||||||
&self,
|
|
||||||
platform: Option<&DynPlatform>,
|
|
||||||
global_params: &GlobalParams,
|
|
||||||
) -> clap::error::Result<Device> {
|
|
||||||
if let Some(device) = self.device {
|
|
||||||
return Ok(device);
|
|
||||||
}
|
|
||||||
if let Some(device) =
|
|
||||||
platform.and_then(|platform| platform.aspects().get_single_by_type::<Device>().copied())
|
|
||||||
{
|
|
||||||
return Ok(device);
|
|
||||||
}
|
|
||||||
Err(global_params.clap_error(
|
|
||||||
clap::error::ErrorKind::MissingRequiredArgument,
|
|
||||||
"missing --device option",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToArgs for XilinxArgs {
|
|
||||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
|
||||||
if let Some(device) = self.device {
|
|
||||||
args.write_long_option_eq("device", device.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! make_device_enum {
|
|
||||||
($vis:vis enum $Device:ident {
|
|
||||||
$(
|
|
||||||
#[
|
|
||||||
name = $name:literal,
|
|
||||||
xray_part = $xray_part:literal,
|
|
||||||
xray_device = $xray_device:literal,
|
|
||||||
xray_family = $xray_family:literal,
|
|
||||||
]
|
|
||||||
$variant:ident,
|
|
||||||
)*
|
|
||||||
}) => {
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, ValueEnum)]
|
|
||||||
$vis enum $Device {
|
|
||||||
$(
|
|
||||||
#[value(name = $name, alias = $xray_part)]
|
|
||||||
$variant,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $Device {
|
|
||||||
$vis fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
$(Self::$variant => $name,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$vis fn xray_part(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
$(Self::$variant => $xray_part,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$vis fn xray_device(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
$(Self::$variant => $xray_device,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$vis fn xray_family(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
$(Self::$variant => $xray_family,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DeviceVisitor;
|
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for DeviceVisitor {
|
|
||||||
type Value = $Device;
|
|
||||||
|
|
||||||
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str("a Xilinx device string")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
match $Device::from_str(v, false) {
|
|
||||||
Ok(v) => Ok(v),
|
|
||||||
Err(_) => Err(E::invalid_value(serde::de::Unexpected::Str(v), &self)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
match str::from_utf8(v).ok().and_then(|v| $Device::from_str(v, false).ok()) {
|
|
||||||
Some(v) => Ok(v),
|
|
||||||
None => Err(E::invalid_value(serde::de::Unexpected::Bytes(v), &self)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for $Device {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
deserializer.deserialize_string(DeviceVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for $Device {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
self.as_str().serialize(serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
make_device_enum! {
|
|
||||||
pub enum Device {
|
|
||||||
#[
|
|
||||||
name = "xc7a35ticsg324-1L",
|
|
||||||
xray_part = "xc7a35tcsg324-1",
|
|
||||||
xray_device = "xc7a35t",
|
|
||||||
xray_family = "artix7",
|
|
||||||
]
|
|
||||||
Xc7a35ticsg324_1l,
|
|
||||||
#[
|
|
||||||
name = "xc7a100ticsg324-1L",
|
|
||||||
xray_part = "xc7a100tcsg324-1",
|
|
||||||
xray_device = "xc7a100t",
|
|
||||||
xray_family = "artix7",
|
|
||||||
]
|
|
||||||
Xc7a100ticsg324_1l,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Device {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
|
|
||||||
arty_a7::built_in_job_kinds()
|
|
||||||
.into_iter()
|
|
||||||
.chain(yosys_nextpnr_prjxray::built_in_job_kinds())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> {
|
|
||||||
arty_a7::built_in_platforms()
|
|
||||||
.into_iter()
|
|
||||||
.chain(yosys_nextpnr_prjxray::built_in_platforms())
|
|
||||||
}
|
|
||||||
404
crates/fayalite/src/vendor/xilinx/arty_a7.rs
vendored
404
crates/fayalite/src/vendor/xilinx/arty_a7.rs
vendored
|
|
@ -1,404 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
intern::{Intern, Interned},
|
|
||||||
module::{instance_with_loc, reg_builder_with_loc, wire_with_loc},
|
|
||||||
platform::{
|
|
||||||
DynPlatform, Peripheral, PeripheralRef, Peripherals, PeripheralsBuilderFactory,
|
|
||||||
PeripheralsBuilderFinished, Platform, PlatformAspectSet,
|
|
||||||
peripherals::{ClockInput, Led, RgbLed, Uart},
|
|
||||||
},
|
|
||||||
prelude::*,
|
|
||||||
vendor::xilinx::{
|
|
||||||
Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation,
|
|
||||||
primitives,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use ordered_float::NotNan;
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
macro_rules! arty_a7_platform {
|
|
||||||
(
|
|
||||||
$vis:vis enum $ArtyA7Platform:ident {
|
|
||||||
$(#[name = $name:literal, device = $device:ident]
|
|
||||||
$Variant:ident,)*
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
$vis enum $ArtyA7Platform {
|
|
||||||
$($Variant,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $ArtyA7Platform {
|
|
||||||
$vis const VARIANTS: &'static [Self] = &[$(Self::$Variant,)*];
|
|
||||||
$vis fn device(self) -> Device {
|
|
||||||
match self {
|
|
||||||
$(Self::$Variant => Device::$device,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$vis const fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
$(Self::$Variant => $name,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn get_aspects(self) -> &'static PlatformAspectSet {
|
|
||||||
match self {
|
|
||||||
$(Self::$Variant => {
|
|
||||||
static ASPECTS_SET: OnceLock<PlatformAspectSet> = OnceLock::new();
|
|
||||||
ASPECTS_SET.get_or_init(|| self.make_aspects())
|
|
||||||
})*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
arty_a7_platform! {
|
|
||||||
pub enum ArtyA7Platform {
|
|
||||||
#[name = "arty-a7-35t", device = Xc7a35ticsg324_1l]
|
|
||||||
ArtyA7_35T,
|
|
||||||
#[name = "arty-a7-100t", device = Xc7a100ticsg324_1l]
|
|
||||||
ArtyA7_100T,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ArtyA7Peripherals {
|
|
||||||
clk100_div_pow2: [Peripheral<ClockInput>; 4],
|
|
||||||
rst: Peripheral<Reset>,
|
|
||||||
rst_sync: Peripheral<SyncReset>,
|
|
||||||
ld0: Peripheral<RgbLed>,
|
|
||||||
ld1: Peripheral<RgbLed>,
|
|
||||||
ld2: Peripheral<RgbLed>,
|
|
||||||
ld3: Peripheral<RgbLed>,
|
|
||||||
ld4: Peripheral<Led>,
|
|
||||||
ld5: Peripheral<Led>,
|
|
||||||
ld6: Peripheral<Led>,
|
|
||||||
ld7: Peripheral<Led>,
|
|
||||||
uart: Peripheral<Uart>,
|
|
||||||
// TODO: add rest of peripherals when we need them
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Peripherals for ArtyA7Peripherals {
|
|
||||||
fn append_peripherals<'a>(&'a self, peripherals: &mut Vec<PeripheralRef<'a, CanonicalType>>) {
|
|
||||||
let Self {
|
|
||||||
clk100_div_pow2,
|
|
||||||
rst,
|
|
||||||
rst_sync,
|
|
||||||
ld0,
|
|
||||||
ld1,
|
|
||||||
ld2,
|
|
||||||
ld3,
|
|
||||||
ld4,
|
|
||||||
ld5,
|
|
||||||
ld6,
|
|
||||||
ld7,
|
|
||||||
uart,
|
|
||||||
} = self;
|
|
||||||
clk100_div_pow2.append_peripherals(peripherals);
|
|
||||||
rst.append_peripherals(peripherals);
|
|
||||||
rst_sync.append_peripherals(peripherals);
|
|
||||||
ld0.append_peripherals(peripherals);
|
|
||||||
ld1.append_peripherals(peripherals);
|
|
||||||
ld2.append_peripherals(peripherals);
|
|
||||||
ld3.append_peripherals(peripherals);
|
|
||||||
ld4.append_peripherals(peripherals);
|
|
||||||
ld5.append_peripherals(peripherals);
|
|
||||||
ld6.append_peripherals(peripherals);
|
|
||||||
ld7.append_peripherals(peripherals);
|
|
||||||
uart.append_peripherals(peripherals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArtyA7Platform {
|
|
||||||
fn make_aspects(self) -> PlatformAspectSet {
|
|
||||||
let mut retval = PlatformAspectSet::new();
|
|
||||||
retval.insert_new(self.device());
|
|
||||||
retval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl_module(extern)]
|
|
||||||
fn reset_sync() {
|
|
||||||
#[hdl]
|
|
||||||
let clk: Clock = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let inp: Bool = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let out: SyncReset = m.output();
|
|
||||||
m.annotate_module(BlackBoxInlineAnnotation {
|
|
||||||
path: "fayalite_arty_a7_reset_sync.v".intern(),
|
|
||||||
text: r#"module __fayalite_arty_a7_reset_sync(input clk, input inp, output out);
|
|
||||||
wire reset_0_out;
|
|
||||||
(* ASYNC_REG = "TRUE" *)
|
|
||||||
FDPE #(
|
|
||||||
.INIT(1'b1)
|
|
||||||
) reset_0 (
|
|
||||||
.Q(reset_0_out),
|
|
||||||
.C(clk),
|
|
||||||
.CE(1'b1),
|
|
||||||
.PRE(inp),
|
|
||||||
.D(1'b0)
|
|
||||||
);
|
|
||||||
(* ASYNC_REG = "TRUE" *)
|
|
||||||
FDPE #(
|
|
||||||
.INIT(1'b1)
|
|
||||||
) reset_1 (
|
|
||||||
.Q(out),
|
|
||||||
.C(clk),
|
|
||||||
.CE(1'b1),
|
|
||||||
.PRE(inp),
|
|
||||||
.D(reset_0_out)
|
|
||||||
);
|
|
||||||
endmodule
|
|
||||||
"#
|
|
||||||
.intern(),
|
|
||||||
});
|
|
||||||
m.verilog_name("__fayalite_arty_a7_reset_sync");
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Platform for ArtyA7Platform {
|
|
||||||
type Peripherals = ArtyA7Peripherals;
|
|
||||||
|
|
||||||
fn name(&self) -> Interned<str> {
|
|
||||||
self.as_str().intern()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_peripherals<'builder>(
|
|
||||||
&self,
|
|
||||||
builder_factory: PeripheralsBuilderFactory<'builder>,
|
|
||||||
) -> (Self::Peripherals, PeripheralsBuilderFinished<'builder>) {
|
|
||||||
let mut builder = builder_factory.builder();
|
|
||||||
|
|
||||||
let clk100_div_pow2 = std::array::from_fn(|log2_divisor| {
|
|
||||||
let divisor = 1u64 << log2_divisor;
|
|
||||||
let name = if divisor != 1 {
|
|
||||||
format!("clk100_div_{divisor}")
|
|
||||||
} else {
|
|
||||||
"clk100".into()
|
|
||||||
};
|
|
||||||
builder.input_peripheral(name, ClockInput::new(100e6 / divisor as f64))
|
|
||||||
});
|
|
||||||
builder.add_conflicts(Vec::from_iter(clk100_div_pow2.iter().map(|v| v.id())));
|
|
||||||
(
|
|
||||||
ArtyA7Peripherals {
|
|
||||||
clk100_div_pow2,
|
|
||||||
rst: builder.input_peripheral("rst", Reset),
|
|
||||||
rst_sync: builder.input_peripheral("rst_sync", SyncReset),
|
|
||||||
ld0: builder.output_peripheral("ld0", RgbLed),
|
|
||||||
ld1: builder.output_peripheral("ld1", RgbLed),
|
|
||||||
ld2: builder.output_peripheral("ld2", RgbLed),
|
|
||||||
ld3: builder.output_peripheral("ld3", RgbLed),
|
|
||||||
ld4: builder.output_peripheral("ld4", Led),
|
|
||||||
ld5: builder.output_peripheral("ld5", Led),
|
|
||||||
ld6: builder.output_peripheral("ld6", Led),
|
|
||||||
ld7: builder.output_peripheral("ld7", Led),
|
|
||||||
uart: builder.output_peripheral("uart", Uart),
|
|
||||||
},
|
|
||||||
builder.finish(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source_location(&self) -> SourceLocation {
|
|
||||||
SourceLocation::builtin()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) {
|
|
||||||
let ArtyA7Peripherals {
|
|
||||||
clk100_div_pow2,
|
|
||||||
rst,
|
|
||||||
rst_sync,
|
|
||||||
ld0,
|
|
||||||
ld1,
|
|
||||||
ld2,
|
|
||||||
ld3,
|
|
||||||
ld4,
|
|
||||||
ld5,
|
|
||||||
ld6,
|
|
||||||
ld7,
|
|
||||||
uart,
|
|
||||||
} = peripherals;
|
|
||||||
let make_buffered_input = |name: &str, location: &str, io_standard: &str, invert: bool| {
|
|
||||||
let pin = m.input_with_loc(name, SourceLocation::builtin(), Bool);
|
|
||||||
annotate(
|
|
||||||
pin,
|
|
||||||
XdcLocationAnnotation {
|
|
||||||
location: location.intern(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
annotate(
|
|
||||||
pin,
|
|
||||||
XdcIOStandardAnnotation {
|
|
||||||
value: io_standard.intern(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let buf = instance_with_loc(
|
|
||||||
&format!("{name}_buf"),
|
|
||||||
primitives::IBUF(),
|
|
||||||
SourceLocation::builtin(),
|
|
||||||
);
|
|
||||||
connect(buf.I, pin);
|
|
||||||
if invert { !buf.O } else { buf.O }
|
|
||||||
};
|
|
||||||
let make_buffered_output = |name: &str, location: &str, io_standard: &str| {
|
|
||||||
let pin = m.output_with_loc(name, SourceLocation::builtin(), Bool);
|
|
||||||
annotate(
|
|
||||||
pin,
|
|
||||||
XdcLocationAnnotation {
|
|
||||||
location: location.intern(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
annotate(
|
|
||||||
pin,
|
|
||||||
XdcIOStandardAnnotation {
|
|
||||||
value: io_standard.intern(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let buf = instance_with_loc(
|
|
||||||
&format!("{name}_buf"),
|
|
||||||
primitives::OBUFT(),
|
|
||||||
SourceLocation::builtin(),
|
|
||||||
);
|
|
||||||
connect(pin, buf.O);
|
|
||||||
connect(buf.T, false);
|
|
||||||
buf.I
|
|
||||||
};
|
|
||||||
let mut frequency = clk100_div_pow2[0].ty().frequency();
|
|
||||||
let mut log2_divisor = 0;
|
|
||||||
let mut clk = None;
|
|
||||||
for (cur_log2_divisor, p) in clk100_div_pow2.into_iter().enumerate() {
|
|
||||||
let Some(p) = p.into_used() else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
debug_assert!(
|
|
||||||
clk.is_none(),
|
|
||||||
"conflict-handling logic should ensure at most one clock is used",
|
|
||||||
);
|
|
||||||
frequency = p.ty().frequency();
|
|
||||||
clk = Some(p);
|
|
||||||
log2_divisor = cur_log2_divisor;
|
|
||||||
}
|
|
||||||
let clk100_buf = make_buffered_input("clk100", "E3", "LVCMOS33", false);
|
|
||||||
let startup = instance_with_loc(
|
|
||||||
"startup",
|
|
||||||
primitives::STARTUPE2_default_inputs(),
|
|
||||||
SourceLocation::builtin(),
|
|
||||||
);
|
|
||||||
let clk_global_buf = instance_with_loc(
|
|
||||||
"clk_global_buf",
|
|
||||||
primitives::BUFGCE(),
|
|
||||||
SourceLocation::builtin(),
|
|
||||||
);
|
|
||||||
connect(clk_global_buf.CE, startup.EOS);
|
|
||||||
let mut clk_global_buf_in = clk100_buf.to_clock();
|
|
||||||
for prev_log2_divisor in 0..log2_divisor {
|
|
||||||
let prev_divisor = 1u64 << prev_log2_divisor;
|
|
||||||
let clk_in = wire_with_loc(
|
|
||||||
&format!("clk_div_{prev_divisor}"),
|
|
||||||
SourceLocation::builtin(),
|
|
||||||
Clock,
|
|
||||||
);
|
|
||||||
connect(clk_in, clk_global_buf_in);
|
|
||||||
annotate(
|
|
||||||
clk_in,
|
|
||||||
XdcCreateClockAnnotation {
|
|
||||||
period: NotNan::new(1e9 / (100e6 / prev_divisor as f64))
|
|
||||||
.expect("known to be valid"),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
annotate(clk_in, DontTouchAnnotation);
|
|
||||||
let cd = wire_with_loc(
|
|
||||||
&format!("clk_div_{prev_divisor}_in"),
|
|
||||||
SourceLocation::builtin(),
|
|
||||||
ClockDomain[AsyncReset],
|
|
||||||
);
|
|
||||||
connect(cd.clk, clk_in);
|
|
||||||
connect(cd.rst, (!startup.EOS).to_async_reset());
|
|
||||||
let divider = reg_builder_with_loc("divider", SourceLocation::builtin())
|
|
||||||
.clock_domain(cd)
|
|
||||||
.reset(false)
|
|
||||||
.build();
|
|
||||||
connect(divider, !divider);
|
|
||||||
clk_global_buf_in = divider.to_clock();
|
|
||||||
}
|
|
||||||
connect(clk_global_buf.I, clk_global_buf_in);
|
|
||||||
let clk_out = wire_with_loc("clk_out", SourceLocation::builtin(), Clock);
|
|
||||||
connect(clk_out, clk_global_buf.O);
|
|
||||||
annotate(
|
|
||||||
clk_out,
|
|
||||||
XdcCreateClockAnnotation {
|
|
||||||
period: NotNan::new(1e9 / frequency).expect("known to be valid"),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
annotate(clk_out, DontTouchAnnotation);
|
|
||||||
if let Some(clk) = clk {
|
|
||||||
connect(clk.instance_io_field().clk, clk_out);
|
|
||||||
}
|
|
||||||
let rst_value = {
|
|
||||||
let rst_buf = make_buffered_input("rst", "C2", "LVCMOS33", true);
|
|
||||||
let rst_sync = instance_with_loc("rst_sync", reset_sync(), SourceLocation::builtin());
|
|
||||||
connect(rst_sync.clk, clk_out);
|
|
||||||
connect(rst_sync.inp, rst_buf | !startup.EOS);
|
|
||||||
rst_sync.out
|
|
||||||
};
|
|
||||||
if let Some(rst) = rst.into_used() {
|
|
||||||
connect(rst.instance_io_field(), rst_value.to_reset());
|
|
||||||
}
|
|
||||||
if let Some(rst_sync) = rst_sync.into_used() {
|
|
||||||
connect(rst_sync.instance_io_field(), rst_value);
|
|
||||||
}
|
|
||||||
let rgb_leds = [
|
|
||||||
(ld0, ("G6", "F6", "E1")),
|
|
||||||
(ld1, ("G3", "J4", "G4")),
|
|
||||||
(ld2, ("J3", "J2", "H4")),
|
|
||||||
(ld3, ("K1", "H6", "K2")),
|
|
||||||
];
|
|
||||||
for (rgb_led, (r_loc, g_loc, b_loc)) in rgb_leds {
|
|
||||||
let r = make_buffered_output(&format!("{}_r", rgb_led.name()), r_loc, "LVCMOS33");
|
|
||||||
let g = make_buffered_output(&format!("{}_g", rgb_led.name()), g_loc, "LVCMOS33");
|
|
||||||
let b = make_buffered_output(&format!("{}_b", rgb_led.name()), b_loc, "LVCMOS33");
|
|
||||||
if let Some(rgb_led) = rgb_led.into_used() {
|
|
||||||
connect(r, rgb_led.instance_io_field().r);
|
|
||||||
connect(g, rgb_led.instance_io_field().g);
|
|
||||||
connect(b, rgb_led.instance_io_field().b);
|
|
||||||
} else {
|
|
||||||
connect(r, false);
|
|
||||||
connect(g, false);
|
|
||||||
connect(b, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let leds = [(ld4, "H5"), (ld5, "J5"), (ld6, "T9"), (ld7, "T10")];
|
|
||||||
for (led, loc) in leds {
|
|
||||||
let o = make_buffered_output(&led.name(), loc, "LVCMOS33");
|
|
||||||
if let Some(led) = led.into_used() {
|
|
||||||
connect(o, led.instance_io_field().on);
|
|
||||||
} else {
|
|
||||||
connect(o, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let uart_tx = make_buffered_output("uart_tx", "D10", "LVCMOS33");
|
|
||||||
let uart_rx = make_buffered_input("uart_rx", "A9", "LVCMOS33", false);
|
|
||||||
if let Some(uart) = uart.into_used() {
|
|
||||||
connect(uart_tx, uart.instance_io_field().tx);
|
|
||||||
connect(uart.instance_io_field().rx, uart_rx);
|
|
||||||
} else {
|
|
||||||
connect(uart_tx, true); // idle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn aspects(&self) -> PlatformAspectSet {
|
|
||||||
self.get_aspects().clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
|
|
||||||
[]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = DynPlatform> {
|
|
||||||
ArtyA7Platform::VARIANTS
|
|
||||||
.iter()
|
|
||||||
.map(|&v| DynPlatform::new(v))
|
|
||||||
}
|
|
||||||
50
crates/fayalite/src/vendor/xilinx/primitives.rs
vendored
50
crates/fayalite/src/vendor/xilinx/primitives.rs
vendored
|
|
@ -1,50 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
#[hdl_module(extern)]
|
|
||||||
pub fn IBUF() {
|
|
||||||
m.verilog_name("IBUF");
|
|
||||||
#[hdl]
|
|
||||||
let O: Bool = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let I: Bool = m.input();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl_module(extern)]
|
|
||||||
pub fn OBUFT() {
|
|
||||||
m.verilog_name("OBUFT");
|
|
||||||
#[hdl]
|
|
||||||
let O: Bool = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let I: Bool = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let T: Bool = m.input();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl_module(extern)]
|
|
||||||
pub fn BUFGCE() {
|
|
||||||
m.verilog_name("BUFGCE");
|
|
||||||
#[hdl]
|
|
||||||
let O: Clock = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let CE: Bool = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let I: Clock = m.input();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl_module(extern)]
|
|
||||||
pub fn STARTUPE2_default_inputs() {
|
|
||||||
m.verilog_name("STARTUPE2");
|
|
||||||
#[hdl]
|
|
||||||
let CFGCLK: Clock = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let CFGMCLK: Clock = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let EOS: Bool = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let PREQ: Bool = m.output();
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr},
|
expr::{Expr, Flow, ToExpr},
|
||||||
intern::Interned,
|
intern::Interned,
|
||||||
module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire},
|
module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
|
|
@ -16,16 +16,7 @@ pub struct Wire<T: Type> {
|
||||||
ty: T,
|
ty: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Type> ValueType for Wire<T> {
|
impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
|
||||||
type Type = T;
|
|
||||||
type ValueCategory = ValueCategoryExpr;
|
|
||||||
|
|
||||||
fn ty(&self) -> Self::Type {
|
|
||||||
self.ty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type> fmt::Debug for Wire<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Wire({:?}: ", self.name)?;
|
write!(f, "Wire({:?}: ", self.name)?;
|
||||||
self.ty.fmt(f)?;
|
self.ty.fmt(f)?;
|
||||||
|
|
@ -58,13 +49,14 @@ impl<T: Type> Wire<T> {
|
||||||
ty: T::from_canonical(ty),
|
ty: T::from_canonical(ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[track_caller]
|
pub fn ty(&self) -> T {
|
||||||
|
self.ty
|
||||||
|
}
|
||||||
pub fn new_unchecked(
|
pub fn new_unchecked(
|
||||||
scoped_name: ScopedNameId,
|
scoped_name: ScopedNameId,
|
||||||
source_location: SourceLocation,
|
source_location: SourceLocation,
|
||||||
ty: T,
|
ty: T,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
scoped_name.0.assert_is_name_id();
|
|
||||||
Self {
|
Self {
|
||||||
name: scoped_name,
|
name: scoped_name,
|
||||||
source_location,
|
source_location,
|
||||||
|
|
@ -78,7 +70,7 @@ impl<T: Type> Wire<T> {
|
||||||
self.containing_module_name_id().0
|
self.containing_module_name_id().0
|
||||||
}
|
}
|
||||||
pub fn containing_module_name_id(&self) -> NameId {
|
pub fn containing_module_name_id(&self) -> NameId {
|
||||||
self.name.0.unwrap_name_id()
|
self.name.0
|
||||||
}
|
}
|
||||||
pub fn name(&self) -> Interned<str> {
|
pub fn name(&self) -> Interned<str> {
|
||||||
self.name_id().0
|
self.name_id().0
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +0,0 @@
|
||||||
<!--
|
|
||||||
SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
See Notices.txt for copyright information
|
|
||||||
-->
|
|
||||||
|
|
||||||
`my_test.vcd` is used in the doctest of `fayalite::testing::checked_vcd_output`
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
$timescale 1 ps $end
|
|
||||||
$scope module my_module $end
|
|
||||||
$var wire 8 gAF7X a $end
|
|
||||||
$var wire 8 QS=a/ b $end
|
|
||||||
$upscope $end
|
|
||||||
$enddefinitions $end
|
|
||||||
$dumpvars
|
|
||||||
b0 gAF7X
|
|
||||||
b0 QS=a/
|
|
||||||
$end
|
|
||||||
#1000000
|
|
||||||
b1100100 gAF7X
|
|
||||||
b101010 QS=a/
|
|
||||||
|
|
@ -2,7 +2,19 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
//! Formal tests in Fayalite
|
//! Formal tests in Fayalite
|
||||||
|
|
||||||
use fayalite::prelude::*;
|
use fayalite::{
|
||||||
|
cli::FormalMode,
|
||||||
|
clock::{Clock, ClockDomain},
|
||||||
|
expr::{CastTo, HdlPartialEq},
|
||||||
|
firrtl::ExportOptions,
|
||||||
|
formal::{any_const, any_seq, formal_reset, hdl_assert, hdl_assume},
|
||||||
|
hdl, hdl_module,
|
||||||
|
int::{Bool, DynSize, Size, UInt, UIntType},
|
||||||
|
module::{connect, connect_any, instance, memory, reg_builder, wire},
|
||||||
|
reset::ToReset,
|
||||||
|
testing::assert_formal,
|
||||||
|
ty::StaticType,
|
||||||
|
};
|
||||||
|
|
||||||
/// Test hidden state
|
/// Test hidden state
|
||||||
///
|
///
|
||||||
|
|
@ -107,7 +119,7 @@ mod hidden_state {
|
||||||
FormalMode::Prove,
|
FormalMode::Prove,
|
||||||
16,
|
16,
|
||||||
None,
|
None,
|
||||||
Default::default(),
|
ExportOptions::default(),
|
||||||
);
|
);
|
||||||
// here a couple of cycles is enough
|
// here a couple of cycles is enough
|
||||||
assert_formal(
|
assert_formal(
|
||||||
|
|
@ -116,7 +128,7 @@ mod hidden_state {
|
||||||
FormalMode::Prove,
|
FormalMode::Prove,
|
||||||
2,
|
2,
|
||||||
None,
|
None,
|
||||||
Default::default(),
|
ExportOptions::default(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,7 +242,7 @@ mod memory {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let wr: WritePort<DynSize> = wire(WritePort[n]);
|
let wr: WritePort<DynSize> = wire(WritePort[n]);
|
||||||
connect(wr.addr, any_seq(UInt[n]));
|
connect(wr.addr, any_seq(UInt[n]));
|
||||||
connect(wr.data, any_seq(UInt::<8>::new_static()));
|
connect(wr.data, any_seq(UInt::<8>::TYPE));
|
||||||
connect(wr.en, any_seq(Bool));
|
connect(wr.en, any_seq(Bool));
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let dut = instance(example_sram(n));
|
let dut = instance(example_sram(n));
|
||||||
|
|
@ -277,7 +289,7 @@ mod memory {
|
||||||
FormalMode::Prove,
|
FormalMode::Prove,
|
||||||
2,
|
2,
|
||||||
None,
|
None,
|
||||||
Default::default(),
|
ExportOptions::default(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use fayalite::{
|
||||||
bundle::BundleType,
|
bundle::BundleType,
|
||||||
enum_::EnumType,
|
enum_::EnumType,
|
||||||
int::{BoolOrIntType, IntType},
|
int::{BoolOrIntType, IntType},
|
||||||
|
phantom_const::PhantomConst,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
ty::StaticType,
|
ty::StaticType,
|
||||||
};
|
};
|
||||||
|
|
@ -196,61 +197,3 @@ check_bounds!(CheckBoundsTTT2<#[a, Type] A: BundleType +, #[b, Type] B: Type +,
|
||||||
check_bounds!(CheckBoundsTTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
check_bounds!(CheckBoundsTTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||||
check_bounds!(CheckBoundsTTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
check_bounds!(CheckBoundsTTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||||
check_bounds!(CheckBoundsTTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
check_bounds!(CheckBoundsTTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct MyPhantomConstInner {
|
|
||||||
pub a: usize,
|
|
||||||
pub b: UInt,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated, get(|v| v.a))]
|
|
||||||
pub type GetA<P: PhantomConstGet<MyPhantomConstInner>> = DynSize;
|
|
||||||
|
|
||||||
#[hdl(outline_generated, get(|v| v.b))]
|
|
||||||
pub type GetB<P: PhantomConstGet<MyPhantomConstInner>> = UInt;
|
|
||||||
|
|
||||||
#[hdl(outline_generated, no_static)]
|
|
||||||
pub struct MyTypeWithPhantomConstParameter<P: PhantomConstGet<MyPhantomConstInner>> {
|
|
||||||
pub a: ArrayType<Bool, GetA<P>>,
|
|
||||||
pub b: HdlOption<GetB<P>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
struct MyPrivateType {}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
pub(crate) struct MyPubCrateType {}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
pub struct MyTypeWithPrivateMembers {
|
|
||||||
a: MyPrivateType,
|
|
||||||
pub(crate) b: MyPubCrateType,
|
|
||||||
pub c: Bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
struct MyPrivateTypeWithArg<T> {
|
|
||||||
v: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
pub(crate) struct MyPubCrateTypeWithArg<T> {
|
|
||||||
v: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
pub struct MyTypeWithPrivateMembersWithArg<T> {
|
|
||||||
a: MyPrivateTypeWithArg<T>,
|
|
||||||
pub(crate) b: MyPubCrateTypeWithArg<T>,
|
|
||||||
pub c: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
pub enum EnumWithOnlyOneVariant {
|
|
||||||
A,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
pub enum EnumWithOnlyOneVariant2<T> {
|
|
||||||
A(T),
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
||||||
// See Notices.txt for copyright information
|
|
||||||
use fayalite::{prelude::*, ty::SimValueDebug};
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
struct MyStruct0<T, S: Size> {
|
|
||||||
v: T,
|
|
||||||
a: ArrayType<UInt<8>, S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
#[test]
|
|
||||||
fn check_my_struct0() {
|
|
||||||
let ty = MyStruct0[UInt[8]][3];
|
|
||||||
assert_eq!(
|
|
||||||
format!("{ty:?}"),
|
|
||||||
"MyStruct0 { v: UInt<8>, a: Array<UInt<8>, 3> }",
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", ty.mask_type()),
|
|
||||||
"MaskType<MyStruct0> { v: Bool, a: Array<Bool, 3> }",
|
|
||||||
);
|
|
||||||
let v = #[hdl(sim)]
|
|
||||||
MyStruct0::<_, _> {
|
|
||||||
v: 0x23u8,
|
|
||||||
a: [1u8, 2, 3],
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
format!("{v:?}"),
|
|
||||||
"MyStruct0 { v: 0x23_u8, a: [0x1_u8, 0x2_u8, 0x3_u8] }",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated, custom_debug())]
|
|
||||||
struct MyStruct1<T, S: Size> {
|
|
||||||
v: T,
|
|
||||||
a: ArrayType<UInt<8>, S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, S: Size> fmt::Debug for MyStruct1<T, S> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let Self { v, a } = self;
|
|
||||||
f.debug_struct("Custom<MyStruct1>")
|
|
||||||
.field("v", v)
|
|
||||||
.field("a", a)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, S: Size> SimValueDebug for MyStruct1<T, S> {
|
|
||||||
#[hdl]
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
#[hdl(sim)]
|
|
||||||
let Self { v, a } = value;
|
|
||||||
f.debug_struct("Custom<MyStruct1>")
|
|
||||||
.field("v", &v)
|
|
||||||
.field("a", &a)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
#[test]
|
|
||||||
fn check_my_struct1() {
|
|
||||||
let ty = MyStruct1[UInt[8]][3];
|
|
||||||
assert_eq!(
|
|
||||||
format!("{ty:?}"),
|
|
||||||
"Custom<MyStruct1> { v: UInt<8>, a: Array<UInt<8>, 3> }",
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", ty.mask_type()),
|
|
||||||
"MaskType<MyStruct1> { v: Bool, a: Array<Bool, 3> }",
|
|
||||||
);
|
|
||||||
let v = #[hdl(sim)]
|
|
||||||
MyStruct1::<_, _> {
|
|
||||||
v: 0x23u8,
|
|
||||||
a: [1u8, 2, 3],
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
format!("{v:?}"),
|
|
||||||
"Custom<MyStruct1> { v: 0x23_u8, a: [0x1_u8, 0x2_u8, 0x3_u8] }",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
|
||||||
enum MyEnum0<T, S: Size> {
|
|
||||||
Unit,
|
|
||||||
V(T),
|
|
||||||
A(ArrayType<UInt<8>, S>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
#[test]
|
|
||||||
fn check_my_enum0() {
|
|
||||||
let ty = MyEnum0[UInt[8]][3];
|
|
||||||
assert_eq!(
|
|
||||||
format!("{ty:?}"),
|
|
||||||
"MyEnum0 { Unit: (), V: UInt<8>, A: Array<UInt<8>, 3> }",
|
|
||||||
);
|
|
||||||
let v = #[hdl(sim)]
|
|
||||||
ty.Unit();
|
|
||||||
assert_eq!(format!("{v:?}"), "Unit");
|
|
||||||
let v = #[hdl(sim)]
|
|
||||||
ty.V(0x23u8);
|
|
||||||
assert_eq!(format!("{v:?}"), "V(0x23_u8)");
|
|
||||||
let v = #[hdl(sim)]
|
|
||||||
ty.A([1u8, 2, 3]);
|
|
||||||
assert_eq!(format!("{v:?}"), "A([0x1_u8, 0x2_u8, 0x3_u8])");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl(outline_generated, custom_debug())]
|
|
||||||
enum MyEnum1<T, S: Size> {
|
|
||||||
Unit,
|
|
||||||
V(T),
|
|
||||||
A(ArrayType<UInt<8>, S>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, S: Size> fmt::Debug for MyEnum1<T, S> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let Self { Unit, V, A } = self;
|
|
||||||
f.debug_struct("Custom<MyEnum1>")
|
|
||||||
.field("Unit", Unit)
|
|
||||||
.field("V", V)
|
|
||||||
.field("A", A)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Type, S: Size> SimValueDebug for MyEnum1<T, S> {
|
|
||||||
#[hdl]
|
|
||||||
fn sim_value_debug(
|
|
||||||
value: &<Self as Type>::SimValue,
|
|
||||||
f: &mut fmt::Formatter<'_>,
|
|
||||||
) -> fmt::Result {
|
|
||||||
type SimValueT<T> = <T as Type>::SimValue;
|
|
||||||
match value {
|
|
||||||
SimValueT::<Self>::Unit(_) => f.write_str("MyEnum1::Unit"),
|
|
||||||
SimValueT::<Self>::V(v, _) => f.debug_tuple("MyEnum1::V").field(v).finish(),
|
|
||||||
SimValueT::<Self>::A(a, _) => f.debug_tuple("MyEnum1::A").field(a).finish(),
|
|
||||||
SimValueT::<Self>::Unknown(_) => f.write_str("MyEnum1::Unknown"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
#[test]
|
|
||||||
fn check_my_enum1() {
|
|
||||||
let ty = MyEnum1[UInt[8]][3];
|
|
||||||
assert_eq!(
|
|
||||||
format!("{ty:?}"),
|
|
||||||
"Custom<MyEnum1> { Unit: (), V: UInt<8>, A: Array<UInt<8>, 3> }",
|
|
||||||
);
|
|
||||||
let v = #[hdl(sim)]
|
|
||||||
ty.Unit();
|
|
||||||
assert_eq!(format!("{v:?}"), "MyEnum1::Unit");
|
|
||||||
let v = #[hdl(sim)]
|
|
||||||
ty.V(0x23u8);
|
|
||||||
assert_eq!(format!("{v:?}"), "MyEnum1::V(0x23_u8)");
|
|
||||||
let v = #[hdl(sim)]
|
|
||||||
ty.A([1u8, 2, 3]);
|
|
||||||
assert_eq!(format!("{v:?}"), "MyEnum1::A([0x1_u8, 0x2_u8, 0x3_u8])");
|
|
||||||
}
|
|
||||||
|
|
@ -6,14 +6,13 @@ use fayalite::{
|
||||||
int::{UIntInRange, UIntInRangeInclusive},
|
int::{UIntInRange, UIntInRangeInclusive},
|
||||||
intern::Intern,
|
intern::Intern,
|
||||||
module::transform::simplify_enums::SimplifyEnumsKind,
|
module::transform::simplify_enums::SimplifyEnumsKind,
|
||||||
platform::PlatformIOBuilder,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
reset::ResetType,
|
reset::ResetType,
|
||||||
ty::StaticType,
|
ty::StaticType,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
#[hdl(outline_generated, cmp_eq)]
|
#[hdl(outline_generated)]
|
||||||
pub enum TestEnum {
|
pub enum TestEnum {
|
||||||
A,
|
A,
|
||||||
B(UInt<8>),
|
B(UInt<8>),
|
||||||
|
|
@ -215,7 +214,7 @@ where
|
||||||
let o: Array<T, N> = m.output();
|
let o: Array<T, N> = m.output();
|
||||||
let bytes = v.to_string().as_bytes().to_expr();
|
let bytes = v.to_string().as_bytes().to_expr();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let o2: Array<UInt<8>> = m.output(bytes.ty());
|
let o2: Array<UInt<8>> = m.output(Expr::ty(bytes));
|
||||||
connect(
|
connect(
|
||||||
o,
|
o,
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -679,224 +678,6 @@ circuit check_enum_literals:
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl_module(outline_generated)]
|
|
||||||
pub fn check_enum_cmp_eq() {
|
|
||||||
#[hdl]
|
|
||||||
let lhs: TestEnum = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let rhs: TestEnum = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let eq: Bool = m.output();
|
|
||||||
connect(eq, lhs.cmp_eq(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_enum_cmp_eq() {
|
|
||||||
let _n = SourceLocation::normalize_files_for_tests();
|
|
||||||
let m = check_enum_cmp_eq();
|
|
||||||
dbg!(m);
|
|
||||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
|
||||||
assert_export_firrtl! {
|
|
||||||
m =>
|
|
||||||
options: ExportOptions {
|
|
||||||
simplify_enums: None,
|
|
||||||
..ExportOptions::default()
|
|
||||||
},
|
|
||||||
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
|
|
||||||
circuit check_enum_cmp_eq:
|
|
||||||
type Ty0 = {|A, B: UInt<8>, C: UInt<1>[3]|}
|
|
||||||
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
input lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
|
|
||||||
input rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
|
|
||||||
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
|
||||||
wire _cast_enum_to_bits_expr: UInt<10>
|
|
||||||
match lhs:
|
|
||||||
A:
|
|
||||||
connect _cast_enum_to_bits_expr, UInt<10>(0)
|
|
||||||
B(_cast_enum_to_bits_expr_B):
|
|
||||||
connect _cast_enum_to_bits_expr, pad(cat(_cast_enum_to_bits_expr_B, UInt<2>(1)), 10)
|
|
||||||
C(_cast_enum_to_bits_expr_C):
|
|
||||||
wire _cast_array_to_bits_expr: UInt<1>[3]
|
|
||||||
connect _cast_array_to_bits_expr[0], _cast_enum_to_bits_expr_C[0]
|
|
||||||
connect _cast_array_to_bits_expr[1], _cast_enum_to_bits_expr_C[1]
|
|
||||||
connect _cast_array_to_bits_expr[2], _cast_enum_to_bits_expr_C[2]
|
|
||||||
wire _cast_to_bits_expr: UInt<3>
|
|
||||||
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
|
|
||||||
connect _cast_enum_to_bits_expr, pad(cat(_cast_to_bits_expr, UInt<2>(2)), 10)
|
|
||||||
wire _cast_enum_to_bits_expr_1: UInt<10>
|
|
||||||
match rhs:
|
|
||||||
A:
|
|
||||||
connect _cast_enum_to_bits_expr_1, UInt<10>(0)
|
|
||||||
B(_cast_enum_to_bits_expr_B_1):
|
|
||||||
connect _cast_enum_to_bits_expr_1, pad(cat(_cast_enum_to_bits_expr_B_1, UInt<2>(1)), 10)
|
|
||||||
C(_cast_enum_to_bits_expr_C_1):
|
|
||||||
wire _cast_array_to_bits_expr_1: UInt<1>[3]
|
|
||||||
connect _cast_array_to_bits_expr_1[0], _cast_enum_to_bits_expr_C_1[0]
|
|
||||||
connect _cast_array_to_bits_expr_1[1], _cast_enum_to_bits_expr_C_1[1]
|
|
||||||
connect _cast_array_to_bits_expr_1[2], _cast_enum_to_bits_expr_C_1[2]
|
|
||||||
wire _cast_to_bits_expr_1: UInt<3>
|
|
||||||
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
|
|
||||||
connect _cast_enum_to_bits_expr_1, pad(cat(_cast_to_bits_expr_1, UInt<2>(2)), 10)
|
|
||||||
connect eq, eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 5:1]
|
|
||||||
",
|
|
||||||
};
|
|
||||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
|
||||||
assert_export_firrtl! {
|
|
||||||
m =>
|
|
||||||
options: ExportOptions {
|
|
||||||
simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody),
|
|
||||||
..ExportOptions::default()
|
|
||||||
},
|
|
||||||
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
|
|
||||||
circuit check_enum_cmp_eq:
|
|
||||||
type Ty0 = {|A, B, C|}
|
|
||||||
type Ty1 = {tag: Ty0, body: UInt<8>}
|
|
||||||
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
input lhs: Ty1 @[module-XXXXXXXXXX.rs 2:1]
|
|
||||||
input rhs: Ty1 @[module-XXXXXXXXXX.rs 3:1]
|
|
||||||
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
|
||||||
wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1]
|
|
||||||
connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
wire _cast_enum_to_bits_expr: UInt<2>
|
|
||||||
match lhs.tag:
|
|
||||||
A:
|
|
||||||
connect _cast_enum_to_bits_expr, UInt<2>(0)
|
|
||||||
B:
|
|
||||||
connect _cast_enum_to_bits_expr, UInt<2>(1)
|
|
||||||
C:
|
|
||||||
connect _cast_enum_to_bits_expr, UInt<2>(2)
|
|
||||||
wire _cast_enum_to_bits_expr_1: UInt<2>
|
|
||||||
match rhs.tag:
|
|
||||||
A:
|
|
||||||
connect _cast_enum_to_bits_expr_1, UInt<2>(0)
|
|
||||||
B:
|
|
||||||
connect _cast_enum_to_bits_expr_1, UInt<2>(1)
|
|
||||||
C:
|
|
||||||
connect _cast_enum_to_bits_expr_1, UInt<2>(2)
|
|
||||||
when eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1): @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
match lhs.tag: @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
A:
|
|
||||||
connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
B:
|
|
||||||
connect __enum_structural_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
C:
|
|
||||||
wire _cast_bits_to_array_expr: UInt<1>[3]
|
|
||||||
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0)
|
|
||||||
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1)
|
|
||||||
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2)
|
|
||||||
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
|
|
||||||
wire _cast_bits_to_array_expr_1: UInt<1>[3]
|
|
||||||
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0)
|
|
||||||
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1)
|
|
||||||
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2)
|
|
||||||
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
|
|
||||||
wire _array_structural_eq: UInt<1>
|
|
||||||
connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]))
|
|
||||||
wire _array_structural_eq_1: UInt<1>
|
|
||||||
connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2]))
|
|
||||||
connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
",
|
|
||||||
};
|
|
||||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
|
||||||
assert_export_firrtl! {
|
|
||||||
m =>
|
|
||||||
options: ExportOptions {
|
|
||||||
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
|
|
||||||
..ExportOptions::default()
|
|
||||||
},
|
|
||||||
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
|
|
||||||
circuit check_enum_cmp_eq:
|
|
||||||
type Ty0 = {tag: UInt<2>, body: UInt<8>}
|
|
||||||
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
input lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
|
|
||||||
input rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
|
|
||||||
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
|
||||||
wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1]
|
|
||||||
connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
when eq(lhs.tag, rhs.tag): @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
when eq(lhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
else when eq(lhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
connect __enum_structural_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
else:
|
|
||||||
wire _cast_bits_to_array_expr: UInt<1>[3]
|
|
||||||
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0)
|
|
||||||
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1)
|
|
||||||
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2)
|
|
||||||
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
|
|
||||||
wire _cast_bits_to_array_expr_1: UInt<1>[3]
|
|
||||||
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0)
|
|
||||||
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1)
|
|
||||||
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2)
|
|
||||||
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
|
|
||||||
wire _array_structural_eq: UInt<1>
|
|
||||||
connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]))
|
|
||||||
wire _array_structural_eq_1: UInt<1>
|
|
||||||
connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2]))
|
|
||||||
connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
",
|
|
||||||
};
|
|
||||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
|
||||||
assert_export_firrtl! {
|
|
||||||
m =>
|
|
||||||
options: ExportOptions {
|
|
||||||
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithUInt),
|
|
||||||
..ExportOptions::default()
|
|
||||||
},
|
|
||||||
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
|
|
||||||
circuit check_enum_cmp_eq:
|
|
||||||
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
input lhs: UInt<10> @[module-XXXXXXXXXX.rs 2:1]
|
|
||||||
input rhs: UInt<10> @[module-XXXXXXXXXX.rs 3:1]
|
|
||||||
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
|
||||||
wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1]
|
|
||||||
connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
when eq(bits(lhs, 1, 0), bits(rhs, 1, 0)): @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
when eq(bits(lhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
else when eq(bits(lhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
connect __enum_structural_eq, eq(bits(bits(lhs, 9, 2), 7, 0), bits(bits(rhs, 9, 2), 7, 0)) @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
else:
|
|
||||||
wire _cast_bits_to_array_expr: UInt<1>[3]
|
|
||||||
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[0], bits(bits(bits(lhs, 9, 2), 2, 0), 0, 0)
|
|
||||||
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[1], bits(bits(bits(lhs, 9, 2), 2, 0), 1, 1)
|
|
||||||
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
|
|
||||||
connect _cast_bits_to_array_expr_flattened[2], bits(bits(bits(lhs, 9, 2), 2, 0), 2, 2)
|
|
||||||
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
|
|
||||||
wire _cast_bits_to_array_expr_1: UInt<1>[3]
|
|
||||||
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(bits(rhs, 9, 2), 2, 0), 0, 0)
|
|
||||||
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(bits(rhs, 9, 2), 2, 0), 1, 1)
|
|
||||||
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
|
|
||||||
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(bits(rhs, 9, 2), 2, 0), 2, 2)
|
|
||||||
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
|
|
||||||
wire _array_structural_eq: UInt<1>
|
|
||||||
connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]))
|
|
||||||
wire _array_structural_eq_1: UInt<1>
|
|
||||||
connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2]))
|
|
||||||
connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl_module(outline_generated)]
|
#[hdl_module(outline_generated)]
|
||||||
pub fn check_struct_enum_match() {
|
pub fn check_struct_enum_match() {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -1394,7 +1175,7 @@ pub fn check_memory_init() {
|
||||||
let waddr2: UInt<4> = m.input();
|
let waddr2: UInt<4> = m.input();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let wdata2: UInt<31> = m.input();
|
let wdata2: UInt<31> = m.input();
|
||||||
let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static::<UInt<31>>()));
|
let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static()));
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let mut mem2 = memory_with_init(mem_init2);
|
let mut mem2 = memory_with_init(mem_init2);
|
||||||
let read_port2 = mem2.new_read_port();
|
let read_port2 = mem2.new_read_port();
|
||||||
|
|
@ -2002,7 +1783,7 @@ pub fn check_memory_of_bundle_of_arrays() {
|
||||||
[(i * i).wrapping_mul(2), (i * i).wrapping_mul(3)],
|
[(i * i).wrapping_mul(2), (i * i).wrapping_mul(3)],
|
||||||
[(i * i).wrapping_mul(i), i * 2],
|
[(i * i).wrapping_mul(i), i * 2],
|
||||||
],
|
],
|
||||||
i.cast_to_static::<UInt<2>>(),
|
i.cast_to_static(),
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -2172,9 +1953,9 @@ pub fn check_memory_of_array_of_bundle() {
|
||||||
let clk: Clock = m.input();
|
let clk: Clock = m.input();
|
||||||
let mem_init = Vec::from_iter((0..0x10u8).map(|i| {
|
let mem_init = Vec::from_iter((0..0x10u8).map(|i| {
|
||||||
[
|
[
|
||||||
(i, i.cast_to_static::<SInt<1>>()),
|
(i, i.cast_to_static()),
|
||||||
((i * i), (i / 2).cast_to_static::<SInt<1>>()),
|
((i * i), (i / 2).cast_to_static()),
|
||||||
((i * i).wrapping_mul(i), (i / 4).cast_to_static::<SInt<1>>()),
|
((i * i).wrapping_mul(i), (i / 4).cast_to_static()),
|
||||||
]
|
]
|
||||||
}));
|
}));
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -2487,8 +2268,7 @@ pub fn check_memory_of_bundle() {
|
||||||
let wmask: (Bool, Bool) = m.input();
|
let wmask: (Bool, Bool) = m.input();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let clk: Clock = m.input();
|
let clk: Clock = m.input();
|
||||||
let mem_init =
|
let mem_init = Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static())));
|
||||||
Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static::<SInt<1>>())));
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let mut mem = memory_with_init(mem_init);
|
let mut mem = memory_with_init(mem_init);
|
||||||
let read_port = mem.new_read_port();
|
let read_port = mem.new_read_port();
|
||||||
|
|
@ -3637,176 +3417,20 @@ circuit check_formal: %[[
|
||||||
input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
|
input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
|
||||||
input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1]
|
input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1]
|
||||||
input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
||||||
inst formal_reset of formal_reset @[builtin 1:1]
|
inst formal_reset of formal_reset @[formal.rs 185:24]
|
||||||
assert(clk, pred1, and(en1, not(formal_reset.rst)), "en check 1") @[module-XXXXXXXXXX.rs 9:1]
|
assert(clk, pred1, and(en1, not(formal_reset.rst)), "en check 1") @[module-XXXXXXXXXX.rs 9:1]
|
||||||
assume(clk, pred2, and(en2, not(formal_reset.rst)), "en check 2") @[module-XXXXXXXXXX.rs 10:1]
|
inst formal_reset_1 of formal_reset @[formal.rs 185:24]
|
||||||
cover(clk, pred3, and(en3, not(formal_reset.rst)), "en check 3") @[module-XXXXXXXXXX.rs 11:1]
|
assume(clk, pred2, and(en2, not(formal_reset_1.rst)), "en check 2") @[module-XXXXXXXXXX.rs 10:1]
|
||||||
assert(clk, pred1, and(UInt<1>(0h1), not(formal_reset.rst)), "check 1") @[module-XXXXXXXXXX.rs 12:1]
|
inst formal_reset_2 of formal_reset @[formal.rs 185:24]
|
||||||
assume(clk, pred2, and(UInt<1>(0h1), not(formal_reset.rst)), "check 2") @[module-XXXXXXXXXX.rs 13:1]
|
cover(clk, pred3, and(en3, not(formal_reset_2.rst)), "en check 3") @[module-XXXXXXXXXX.rs 11:1]
|
||||||
cover(clk, pred3, and(UInt<1>(0h1), not(formal_reset.rst)), "check 3") @[module-XXXXXXXXXX.rs 14:1]
|
inst formal_reset_3 of formal_reset @[formal.rs 185:24]
|
||||||
extmodule formal_reset: @[builtin 1:1]
|
assert(clk, pred1, and(UInt<1>(0h1), not(formal_reset_3.rst)), "check 1") @[module-XXXXXXXXXX.rs 12:1]
|
||||||
output rst: UInt<1> @[builtin 1:1]
|
inst formal_reset_4 of formal_reset @[formal.rs 185:24]
|
||||||
defname = __fayalite_formal_reset
|
assume(clk, pred2, and(UInt<1>(0h1), not(formal_reset_4.rst)), "check 2") @[module-XXXXXXXXXX.rs 13:1]
|
||||||
"#,
|
inst formal_reset_5 of formal_reset @[formal.rs 185:24]
|
||||||
};
|
cover(clk, pred3, and(UInt<1>(0h1), not(formal_reset_5.rst)), "check 3") @[module-XXXXXXXXXX.rs 14:1]
|
||||||
}
|
extmodule formal_reset: @[formal.rs 169:5]
|
||||||
|
output rst: UInt<1> @[formal.rs 172:32]
|
||||||
#[hdl_module(outline_generated)]
|
|
||||||
pub fn check_formal_input() {
|
|
||||||
#[hdl]
|
|
||||||
let bool_in: Bool = m.input();
|
|
||||||
#[hdl]
|
|
||||||
let bool_out: Bool = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let any_const_out1: Bool = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let any_const_out2: UInt<16> = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let any_const_out3: SInt<12> = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let any_seq_out: UInt<10> = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let all_const_out: UInt<10> = m.output();
|
|
||||||
#[hdl]
|
|
||||||
let all_seq_out: UInt<10> = m.output();
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
let bool_reg = reg_builder()
|
|
||||||
.clock_domain(
|
|
||||||
#[hdl]
|
|
||||||
ClockDomain {
|
|
||||||
clk: formal_global_clock(),
|
|
||||||
rst: formal_reset(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.reset(false);
|
|
||||||
|
|
||||||
connect(bool_reg, bool_in);
|
|
||||||
connect(bool_out, bool_reg);
|
|
||||||
connect(any_const_out1, any_const(StaticType::TYPE));
|
|
||||||
connect(any_const_out2, any_const(StaticType::TYPE));
|
|
||||||
connect(any_const_out3, any_const(StaticType::TYPE));
|
|
||||||
connect(any_seq_out, any_seq(StaticType::TYPE));
|
|
||||||
connect(all_const_out, all_const(StaticType::TYPE));
|
|
||||||
connect(all_seq_out, all_seq(StaticType::TYPE));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_formal_input() {
|
|
||||||
let _n = SourceLocation::normalize_files_for_tests();
|
|
||||||
let m = check_formal_input();
|
|
||||||
dbg!(m);
|
|
||||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
|
||||||
assert_export_firrtl! {
|
|
||||||
m =>
|
|
||||||
"/test/check_formal_input.fir": r#"FIRRTL version 3.2.0
|
|
||||||
circuit check_formal_input: %[[
|
|
||||||
{
|
|
||||||
"class": "firrtl.AttributeAnnotation",
|
|
||||||
"description": "gclk",
|
|
||||||
"target": "~check_formal_input|check_formal_input>formal_global_clock"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.transforms.DontTouchAnnotation",
|
|
||||||
"target": "~check_formal_input|check_formal_input>formal_global_clock"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.AttributeAnnotation",
|
|
||||||
"description": "anyconst",
|
|
||||||
"target": "~check_formal_input|check_formal_input>any_const"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.transforms.DontTouchAnnotation",
|
|
||||||
"target": "~check_formal_input|check_formal_input>any_const"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.AttributeAnnotation",
|
|
||||||
"description": "anyconst",
|
|
||||||
"target": "~check_formal_input|check_formal_input>any_const_1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.transforms.DontTouchAnnotation",
|
|
||||||
"target": "~check_formal_input|check_formal_input>any_const_1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.AttributeAnnotation",
|
|
||||||
"description": "anyconst",
|
|
||||||
"target": "~check_formal_input|check_formal_input>any_const_2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.transforms.DontTouchAnnotation",
|
|
||||||
"target": "~check_formal_input|check_formal_input>any_const_2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.AttributeAnnotation",
|
|
||||||
"description": "anyseq",
|
|
||||||
"target": "~check_formal_input|check_formal_input>any_seq"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.transforms.DontTouchAnnotation",
|
|
||||||
"target": "~check_formal_input|check_formal_input>any_seq"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.AttributeAnnotation",
|
|
||||||
"description": "allconst",
|
|
||||||
"target": "~check_formal_input|check_formal_input>all_const"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.transforms.DontTouchAnnotation",
|
|
||||||
"target": "~check_formal_input|check_formal_input>all_const"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.AttributeAnnotation",
|
|
||||||
"description": "allseq",
|
|
||||||
"target": "~check_formal_input|check_formal_input>all_seq"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.transforms.DontTouchAnnotation",
|
|
||||||
"target": "~check_formal_input|check_formal_input>all_seq"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": "firrtl.transforms.BlackBoxInlineAnno",
|
|
||||||
"name": "fayalite_formal_reset.v",
|
|
||||||
"text": "module __fayalite_formal_reset(output rst);\n assign rst = $initstate;\nendmodule\n",
|
|
||||||
"target": "~check_formal_input|formal_reset"
|
|
||||||
}
|
|
||||||
]]
|
|
||||||
type Ty0 = {clk: Clock, rst: UInt<1>}
|
|
||||||
type Ty1 = {rst: UInt<1>}
|
|
||||||
module check_formal_input: @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
input bool_in: UInt<1> @[module-XXXXXXXXXX.rs 2:1]
|
|
||||||
output bool_out: UInt<1> @[module-XXXXXXXXXX.rs 3:1]
|
|
||||||
output any_const_out1: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
|
||||||
output any_const_out2: UInt<16> @[module-XXXXXXXXXX.rs 5:1]
|
|
||||||
output any_const_out3: SInt<12> @[module-XXXXXXXXXX.rs 6:1]
|
|
||||||
output any_seq_out: UInt<10> @[module-XXXXXXXXXX.rs 7:1]
|
|
||||||
output all_const_out: UInt<10> @[module-XXXXXXXXXX.rs 8:1]
|
|
||||||
output all_seq_out: UInt<10> @[module-XXXXXXXXXX.rs 9:1]
|
|
||||||
wire _bundle_literal_expr_1: Ty0
|
|
||||||
connect _bundle_literal_expr_1.clk, asClock(UInt<1>(0h0))
|
|
||||||
connect _bundle_literal_expr_1.rst, UInt<1>(0h0)
|
|
||||||
reg formal_global_clock: UInt<1>, _bundle_literal_expr_1.clk @[builtin 1:1]
|
|
||||||
inst formal_reset of formal_reset @[builtin 1:1]
|
|
||||||
reg any_const: UInt<1>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 13:1]
|
|
||||||
reg any_const_1: UInt<16>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 15:1]
|
|
||||||
reg any_const_2: SInt<12>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 17:1]
|
|
||||||
reg any_seq: UInt<10>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 19:1]
|
|
||||||
reg all_const: UInt<10>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 21:1]
|
|
||||||
reg all_seq: UInt<10>, _bundle_literal_expr_1.clk @[module-XXXXXXXXXX.rs 23:1]
|
|
||||||
wire _bundle_literal_expr: Ty0
|
|
||||||
connect _bundle_literal_expr.clk, asClock(formal_global_clock)
|
|
||||||
connect _bundle_literal_expr.rst, formal_reset.rst
|
|
||||||
regreset bool_reg: UInt<1>, _bundle_literal_expr.clk, _bundle_literal_expr.rst, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 10:1]
|
|
||||||
connect bool_reg, bool_in @[module-XXXXXXXXXX.rs 11:1]
|
|
||||||
connect bool_out, bool_reg @[module-XXXXXXXXXX.rs 12:1]
|
|
||||||
connect any_const_out1, any_const @[module-XXXXXXXXXX.rs 14:1]
|
|
||||||
connect any_const_out2, any_const_1 @[module-XXXXXXXXXX.rs 16:1]
|
|
||||||
connect any_const_out3, any_const_2 @[module-XXXXXXXXXX.rs 18:1]
|
|
||||||
connect any_seq_out, any_seq @[module-XXXXXXXXXX.rs 20:1]
|
|
||||||
connect all_const_out, all_const @[module-XXXXXXXXXX.rs 22:1]
|
|
||||||
connect all_seq_out, all_seq @[module-XXXXXXXXXX.rs 24:1]
|
|
||||||
extmodule formal_reset: @[builtin 1:1]
|
|
||||||
output rst: UInt<1> @[builtin 1:1]
|
|
||||||
defname = __fayalite_formal_reset
|
defname = __fayalite_formal_reset
|
||||||
"#,
|
"#,
|
||||||
};
|
};
|
||||||
|
|
@ -3939,10 +3563,21 @@ circuit check_enum_connect_any:
|
||||||
connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1]
|
connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
HdlSome:
|
HdlSome:
|
||||||
wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
||||||
|
wire _cast_bits_to_bundle_expr_1: Ty5
|
||||||
|
wire _cast_bits_to_bundle_expr_flattened_1: Ty6
|
||||||
|
connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 2, 0), 0, 0)
|
||||||
|
wire _cast_bits_to_enum_expr_1: Ty3
|
||||||
|
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_1.tag, 0)):
|
||||||
|
connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlNone)
|
||||||
|
else:
|
||||||
|
connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlSome)
|
||||||
|
connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_enum_expr_1
|
||||||
|
connect _cast_bits_to_bundle_expr_flattened_1.body, bits(bits(i2.body, 2, 0), 2, 1)
|
||||||
|
connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body
|
||||||
; connect different types:
|
; connect different types:
|
||||||
; lhs: SInt<1>
|
; lhs: SInt<1>
|
||||||
; rhs: SInt<2>
|
; rhs: SInt<2>
|
||||||
connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1]
|
connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr_1.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1]
|
||||||
wire _bundle_literal_expr_2: Ty4
|
wire _bundle_literal_expr_2: Ty4
|
||||||
connect _bundle_literal_expr_2.tag, {|HdlNone, HdlSome|}(HdlSome)
|
connect _bundle_literal_expr_2.tag, {|HdlNone, HdlSome|}(HdlSome)
|
||||||
connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2)
|
connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2)
|
||||||
|
|
@ -3964,18 +3599,18 @@ circuit check_enum_connect_any:
|
||||||
connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1]
|
connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
C:
|
C:
|
||||||
wire __connect_variant_body_3: Ty8 @[module-XXXXXXXXXX.rs 8:1]
|
wire __connect_variant_body_3: Ty8 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
wire _cast_bits_to_bundle_expr_1: Ty8
|
wire _cast_bits_to_bundle_expr_2: Ty8
|
||||||
wire _cast_bits_to_bundle_expr_flattened_1: Ty9
|
wire _cast_bits_to_bundle_expr_flattened_2: Ty9
|
||||||
connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 0, 0), 0, 0)
|
connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i2.body, 0, 0), 0, 0)
|
||||||
wire _cast_bits_to_enum_expr_1: Ty3
|
wire _cast_bits_to_enum_expr_2: Ty3
|
||||||
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_1.tag, 0)):
|
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_2.tag, 0)):
|
||||||
connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlNone)
|
connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlNone)
|
||||||
else:
|
else:
|
||||||
connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlSome)
|
connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlSome)
|
||||||
connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_enum_expr_1
|
connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_enum_expr_2
|
||||||
connect _cast_bits_to_bundle_expr_flattened_1.body, UInt<0>(0)
|
connect _cast_bits_to_bundle_expr_flattened_2.body, UInt<0>(0)
|
||||||
connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body
|
connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body
|
||||||
connect __connect_variant_body_3, _cast_bits_to_bundle_expr_1 @[module-XXXXXXXXXX.rs 8:1]
|
connect __connect_variant_body_3, _cast_bits_to_bundle_expr_2 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
wire _bundle_literal_expr_4: Ty1
|
wire _bundle_literal_expr_4: Ty1
|
||||||
connect _bundle_literal_expr_4.tag, {|A, B, C|}(C)
|
connect _bundle_literal_expr_4.tag, {|A, B, C|}(C)
|
||||||
wire _cast_bundle_to_bits_expr_1: Ty9
|
wire _cast_bundle_to_bits_expr_1: Ty9
|
||||||
|
|
@ -4004,18 +3639,18 @@ circuit check_enum_connect_any:
|
||||||
connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1]
|
connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
B:
|
B:
|
||||||
wire __connect_variant_body_5: Ty5 @[module-XXXXXXXXXX.rs 9:1]
|
wire __connect_variant_body_5: Ty5 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _cast_bits_to_bundle_expr_2: Ty4
|
wire _cast_bits_to_bundle_expr_3: Ty4
|
||||||
wire _cast_bits_to_bundle_expr_flattened_2: Ty7
|
wire _cast_bits_to_bundle_expr_flattened_3: Ty7
|
||||||
connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i1.body, 1, 0), 0, 0)
|
connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 1, 0), 0, 0)
|
||||||
wire _cast_bits_to_enum_expr_2: Ty3
|
wire _cast_bits_to_enum_expr_3: Ty3
|
||||||
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_2.tag, 0)):
|
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_3.tag, 0)):
|
||||||
connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlNone)
|
connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlNone)
|
||||||
else:
|
else:
|
||||||
connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlSome)
|
connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlSome)
|
||||||
connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_enum_expr_2
|
connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_enum_expr_3
|
||||||
connect _cast_bits_to_bundle_expr_flattened_2.body, bits(bits(i1.body, 1, 0), 1, 1)
|
connect _cast_bits_to_bundle_expr_flattened_3.body, bits(bits(i1.body, 1, 0), 1, 1)
|
||||||
connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body
|
connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body
|
||||||
match _cast_bits_to_bundle_expr_2.tag: @[module-XXXXXXXXXX.rs 9:1]
|
match _cast_bits_to_bundle_expr_3.tag: @[module-XXXXXXXXXX.rs 9:1]
|
||||||
HdlNone:
|
HdlNone:
|
||||||
wire _bundle_literal_expr_6: Ty5
|
wire _bundle_literal_expr_6: Ty5
|
||||||
connect _bundle_literal_expr_6.tag, {|HdlNone, HdlSome|}(HdlNone)
|
connect _bundle_literal_expr_6.tag, {|HdlNone, HdlSome|}(HdlNone)
|
||||||
|
|
@ -4023,10 +3658,21 @@ circuit check_enum_connect_any:
|
||||||
connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1]
|
connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
HdlSome:
|
HdlSome:
|
||||||
wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1]
|
wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1]
|
||||||
|
wire _cast_bits_to_bundle_expr_4: Ty4
|
||||||
|
wire _cast_bits_to_bundle_expr_flattened_4: Ty7
|
||||||
|
connect _cast_bits_to_bundle_expr_flattened_4.tag, bits(bits(i1.body, 1, 0), 0, 0)
|
||||||
|
wire _cast_bits_to_enum_expr_4: Ty3
|
||||||
|
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_4.tag, 0)):
|
||||||
|
connect _cast_bits_to_enum_expr_4, {|HdlNone, HdlSome|}(HdlNone)
|
||||||
|
else:
|
||||||
|
connect _cast_bits_to_enum_expr_4, {|HdlNone, HdlSome|}(HdlSome)
|
||||||
|
connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_enum_expr_4
|
||||||
|
connect _cast_bits_to_bundle_expr_flattened_4.body, bits(bits(i1.body, 1, 0), 1, 1)
|
||||||
|
connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_4.body
|
||||||
; connect different types:
|
; connect different types:
|
||||||
; lhs: SInt<2>
|
; lhs: SInt<2>
|
||||||
; rhs: SInt<1>
|
; rhs: SInt<1>
|
||||||
connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_2.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1]
|
connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_4.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _bundle_literal_expr_7: Ty5
|
wire _bundle_literal_expr_7: Ty5
|
||||||
connect _bundle_literal_expr_7.tag, {|HdlNone, HdlSome|}(HdlSome)
|
connect _bundle_literal_expr_7.tag, {|HdlNone, HdlSome|}(HdlSome)
|
||||||
connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6)
|
connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6)
|
||||||
|
|
@ -4048,18 +3694,18 @@ circuit check_enum_connect_any:
|
||||||
connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1]
|
connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
C:
|
C:
|
||||||
wire __connect_variant_body_7: Ty8 @[module-XXXXXXXXXX.rs 9:1]
|
wire __connect_variant_body_7: Ty8 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _cast_bits_to_bundle_expr_3: Ty8
|
wire _cast_bits_to_bundle_expr_5: Ty8
|
||||||
wire _cast_bits_to_bundle_expr_flattened_3: Ty9
|
wire _cast_bits_to_bundle_expr_flattened_5: Ty9
|
||||||
connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 0, 0), 0, 0)
|
connect _cast_bits_to_bundle_expr_flattened_5.tag, bits(bits(i1.body, 0, 0), 0, 0)
|
||||||
wire _cast_bits_to_enum_expr_3: Ty3
|
wire _cast_bits_to_enum_expr_5: Ty3
|
||||||
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_3.tag, 0)):
|
when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_5.tag, 0)):
|
||||||
connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlNone)
|
connect _cast_bits_to_enum_expr_5, {|HdlNone, HdlSome|}(HdlNone)
|
||||||
else:
|
else:
|
||||||
connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlSome)
|
connect _cast_bits_to_enum_expr_5, {|HdlNone, HdlSome|}(HdlSome)
|
||||||
connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_enum_expr_3
|
connect _cast_bits_to_bundle_expr_5.tag, _cast_bits_to_enum_expr_5
|
||||||
connect _cast_bits_to_bundle_expr_flattened_3.body, UInt<0>(0)
|
connect _cast_bits_to_bundle_expr_flattened_5.body, UInt<0>(0)
|
||||||
connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body
|
connect _cast_bits_to_bundle_expr_5.body, _cast_bits_to_bundle_expr_flattened_5.body
|
||||||
connect __connect_variant_body_7, _cast_bits_to_bundle_expr_3 @[module-XXXXXXXXXX.rs 9:1]
|
connect __connect_variant_body_7, _cast_bits_to_bundle_expr_5 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _bundle_literal_expr_9: Ty2
|
wire _bundle_literal_expr_9: Ty2
|
||||||
connect _bundle_literal_expr_9.tag, {|A, B, C|}(C)
|
connect _bundle_literal_expr_9.tag, {|A, B, C|}(C)
|
||||||
wire _cast_bundle_to_bits_expr_3: Ty9
|
wire _cast_bundle_to_bits_expr_3: Ty9
|
||||||
|
|
@ -4126,10 +3772,16 @@ circuit check_enum_connect_any:
|
||||||
connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1]
|
connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
else:
|
else:
|
||||||
wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
||||||
|
wire _cast_bits_to_bundle_expr_1: Ty3
|
||||||
|
wire _cast_bits_to_bundle_expr_flattened_1: Ty3
|
||||||
|
connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 2, 0), 0, 0)
|
||||||
|
connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_bundle_expr_flattened_1.tag
|
||||||
|
connect _cast_bits_to_bundle_expr_flattened_1.body, bits(bits(i2.body, 2, 0), 2, 1)
|
||||||
|
connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body
|
||||||
; connect different types:
|
; connect different types:
|
||||||
; lhs: SInt<1>
|
; lhs: SInt<1>
|
||||||
; rhs: SInt<2>
|
; rhs: SInt<2>
|
||||||
connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1]
|
connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr_1.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1]
|
||||||
wire _bundle_literal_expr_2: Ty2
|
wire _bundle_literal_expr_2: Ty2
|
||||||
connect _bundle_literal_expr_2.tag, UInt<1>(0h1)
|
connect _bundle_literal_expr_2.tag, UInt<1>(0h1)
|
||||||
connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2)
|
connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2)
|
||||||
|
|
@ -4145,13 +3797,13 @@ circuit check_enum_connect_any:
|
||||||
connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1]
|
connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
else:
|
else:
|
||||||
wire __connect_variant_body_3: Ty4 @[module-XXXXXXXXXX.rs 8:1]
|
wire __connect_variant_body_3: Ty4 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
wire _cast_bits_to_bundle_expr_1: Ty4
|
wire _cast_bits_to_bundle_expr_2: Ty4
|
||||||
wire _cast_bits_to_bundle_expr_flattened_1: Ty4
|
wire _cast_bits_to_bundle_expr_flattened_2: Ty4
|
||||||
connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 0, 0), 0, 0)
|
connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i2.body, 0, 0), 0, 0)
|
||||||
connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_bundle_expr_flattened_1.tag
|
connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_bundle_expr_flattened_2.tag
|
||||||
connect _cast_bits_to_bundle_expr_flattened_1.body, UInt<0>(0)
|
connect _cast_bits_to_bundle_expr_flattened_2.body, UInt<0>(0)
|
||||||
connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body
|
connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body
|
||||||
connect __connect_variant_body_3, _cast_bits_to_bundle_expr_1 @[module-XXXXXXXXXX.rs 8:1]
|
connect __connect_variant_body_3, _cast_bits_to_bundle_expr_2 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
wire _bundle_literal_expr_4: Ty0
|
wire _bundle_literal_expr_4: Ty0
|
||||||
connect _bundle_literal_expr_4.tag, UInt<2>(0h2)
|
connect _bundle_literal_expr_4.tag, UInt<2>(0h2)
|
||||||
wire _cast_bundle_to_bits_expr_1: Ty4
|
wire _cast_bundle_to_bits_expr_1: Ty4
|
||||||
|
|
@ -4173,23 +3825,29 @@ circuit check_enum_connect_any:
|
||||||
connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1]
|
connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
else when eq(i1.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 9:1]
|
else when eq(i1.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire __connect_variant_body_5: Ty3 @[module-XXXXXXXXXX.rs 9:1]
|
wire __connect_variant_body_5: Ty3 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _cast_bits_to_bundle_expr_2: Ty2
|
wire _cast_bits_to_bundle_expr_3: Ty2
|
||||||
wire _cast_bits_to_bundle_expr_flattened_2: Ty2
|
wire _cast_bits_to_bundle_expr_flattened_3: Ty2
|
||||||
connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i1.body, 1, 0), 0, 0)
|
connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 1, 0), 0, 0)
|
||||||
connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_bundle_expr_flattened_2.tag
|
connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_bundle_expr_flattened_3.tag
|
||||||
connect _cast_bits_to_bundle_expr_flattened_2.body, bits(bits(i1.body, 1, 0), 1, 1)
|
connect _cast_bits_to_bundle_expr_flattened_3.body, bits(bits(i1.body, 1, 0), 1, 1)
|
||||||
connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body
|
connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body
|
||||||
when eq(_cast_bits_to_bundle_expr_2.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 9:1]
|
when eq(_cast_bits_to_bundle_expr_3.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _bundle_literal_expr_6: Ty3
|
wire _bundle_literal_expr_6: Ty3
|
||||||
connect _bundle_literal_expr_6.tag, UInt<1>(0h0)
|
connect _bundle_literal_expr_6.tag, UInt<1>(0h0)
|
||||||
connect _bundle_literal_expr_6.body, UInt<2>(0h0)
|
connect _bundle_literal_expr_6.body, UInt<2>(0h0)
|
||||||
connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1]
|
connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
else:
|
else:
|
||||||
wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1]
|
wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1]
|
||||||
|
wire _cast_bits_to_bundle_expr_4: Ty2
|
||||||
|
wire _cast_bits_to_bundle_expr_flattened_4: Ty2
|
||||||
|
connect _cast_bits_to_bundle_expr_flattened_4.tag, bits(bits(i1.body, 1, 0), 0, 0)
|
||||||
|
connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_bundle_expr_flattened_4.tag
|
||||||
|
connect _cast_bits_to_bundle_expr_flattened_4.body, bits(bits(i1.body, 1, 0), 1, 1)
|
||||||
|
connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_4.body
|
||||||
; connect different types:
|
; connect different types:
|
||||||
; lhs: SInt<2>
|
; lhs: SInt<2>
|
||||||
; rhs: SInt<1>
|
; rhs: SInt<1>
|
||||||
connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_2.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1]
|
connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_4.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _bundle_literal_expr_7: Ty3
|
wire _bundle_literal_expr_7: Ty3
|
||||||
connect _bundle_literal_expr_7.tag, UInt<1>(0h1)
|
connect _bundle_literal_expr_7.tag, UInt<1>(0h1)
|
||||||
connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6)
|
connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6)
|
||||||
|
|
@ -4205,13 +3863,13 @@ circuit check_enum_connect_any:
|
||||||
connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1]
|
connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
else:
|
else:
|
||||||
wire __connect_variant_body_7: Ty4 @[module-XXXXXXXXXX.rs 9:1]
|
wire __connect_variant_body_7: Ty4 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _cast_bits_to_bundle_expr_3: Ty4
|
wire _cast_bits_to_bundle_expr_5: Ty4
|
||||||
wire _cast_bits_to_bundle_expr_flattened_3: Ty4
|
wire _cast_bits_to_bundle_expr_flattened_5: Ty4
|
||||||
connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 0, 0), 0, 0)
|
connect _cast_bits_to_bundle_expr_flattened_5.tag, bits(bits(i1.body, 0, 0), 0, 0)
|
||||||
connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_bundle_expr_flattened_3.tag
|
connect _cast_bits_to_bundle_expr_5.tag, _cast_bits_to_bundle_expr_flattened_5.tag
|
||||||
connect _cast_bits_to_bundle_expr_flattened_3.body, UInt<0>(0)
|
connect _cast_bits_to_bundle_expr_flattened_5.body, UInt<0>(0)
|
||||||
connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body
|
connect _cast_bits_to_bundle_expr_5.body, _cast_bits_to_bundle_expr_flattened_5.body
|
||||||
connect __connect_variant_body_7, _cast_bits_to_bundle_expr_3 @[module-XXXXXXXXXX.rs 9:1]
|
connect __connect_variant_body_7, _cast_bits_to_bundle_expr_5 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
wire _bundle_literal_expr_9: Ty1
|
wire _bundle_literal_expr_9: Ty1
|
||||||
connect _bundle_literal_expr_9.tag, UInt<2>(0h2)
|
connect _bundle_literal_expr_9.tag, UInt<2>(0h2)
|
||||||
wire _cast_bundle_to_bits_expr_3: Ty4
|
wire _cast_bundle_to_bits_expr_3: Ty4
|
||||||
|
|
@ -4863,20 +4521,34 @@ circuit check_struct_cmp_eq:
|
||||||
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
|
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
|
||||||
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
|
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
|
||||||
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
|
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
|
||||||
wire _bundle_structural_eq: UInt<1>
|
wire _array_literal_expr: UInt<1>[3]
|
||||||
connect _bundle_structural_eq, and(eq(tuple_lhs.`0`, tuple_rhs.`0`), eq(tuple_lhs.`1`, tuple_rhs.`1`))
|
connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||||
wire _bundle_structural_eq_1: UInt<1>
|
connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||||
connect _bundle_structural_eq_1, and(_bundle_structural_eq, eq(tuple_lhs.`2`, tuple_rhs.`2`))
|
connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||||
connect tuple_cmp_eq, _bundle_structural_eq_1 @[module-XXXXXXXXXX.rs 5:1]
|
wire _cast_array_to_bits_expr: UInt<1>[3]
|
||||||
connect tuple_cmp_ne, not(_bundle_structural_eq_1) @[module-XXXXXXXXXX.rs 7:1]
|
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
|
||||||
wire _bundle_structural_eq_2: UInt<1>
|
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
|
||||||
connect _bundle_structural_eq_2, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b))
|
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
|
||||||
connect test_struct_cmp_eq, _bundle_structural_eq_2 @[module-XXXXXXXXXX.rs 11:1]
|
wire _cast_to_bits_expr: UInt<3>
|
||||||
connect test_struct_cmp_ne, not(_bundle_structural_eq_2) @[module-XXXXXXXXXX.rs 13:1]
|
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
|
||||||
|
connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
|
||||||
|
wire _array_literal_expr_1: UInt<1>[3]
|
||||||
|
connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||||
|
connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||||
|
connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||||
|
wire _cast_array_to_bits_expr_1: UInt<1>[3]
|
||||||
|
connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0]
|
||||||
|
connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1]
|
||||||
|
connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2]
|
||||||
|
wire _cast_to_bits_expr_1: UInt<3>
|
||||||
|
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
|
||||||
|
connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1]
|
||||||
|
connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1]
|
||||||
|
connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1]
|
||||||
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
|
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
|
||||||
connect test_struct_2_cmp_ne, not(eq(test_struct_2_lhs.v, test_struct_2_rhs.v)) @[module-XXXXXXXXXX.rs 19:1]
|
connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1]
|
||||||
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
|
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
|
||||||
connect test_struct_3_cmp_ne, not(UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 25:1]
|
connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1]
|
||||||
",
|
",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -4959,55 +4631,3 @@ circuit check_uint_in_range:
|
||||||
",
|
",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl_module(outline_generated)]
|
|
||||||
pub fn check_platform_io(platform_io_builder: PlatformIOBuilder<'_>) {
|
|
||||||
#[hdl]
|
|
||||||
let io = m.add_platform_io(platform_io_builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(todo)]
|
|
||||||
#[test]
|
|
||||||
fn test_platform_io() {
|
|
||||||
let _n = SourceLocation::normalize_files_for_tests();
|
|
||||||
let m = check_platform_io(todo!());
|
|
||||||
dbg!(m);
|
|
||||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
|
||||||
assert_export_firrtl! {
|
|
||||||
m =>
|
|
||||||
"/test/check_platform_io.fir": r"FIRRTL version 3.2.0
|
|
||||||
circuit check_platform_io:
|
|
||||||
type Ty0 = {value: UInt<0>, range: {}}
|
|
||||||
type Ty1 = {value: UInt<1>, range: {}}
|
|
||||||
type Ty2 = {value: UInt<2>, range: {}}
|
|
||||||
type Ty3 = {value: UInt<2>, range: {}}
|
|
||||||
type Ty4 = {value: UInt<3>, range: {}}
|
|
||||||
type Ty5 = {value: UInt<3>, range: {}}
|
|
||||||
type Ty6 = {value: UInt<4>, range: {}}
|
|
||||||
type Ty7 = {value: UInt<0>, range: {}}
|
|
||||||
type Ty8 = {value: UInt<1>, range: {}}
|
|
||||||
type Ty9 = {value: UInt<2>, range: {}}
|
|
||||||
type Ty10 = {value: UInt<2>, range: {}}
|
|
||||||
type Ty11 = {value: UInt<3>, range: {}}
|
|
||||||
type Ty12 = {value: UInt<3>, range: {}}
|
|
||||||
type Ty13 = {value: UInt<4>, range: {}}
|
|
||||||
type Ty14 = {value: UInt<4>, range: {}}
|
|
||||||
module check_platform_io: @[module-XXXXXXXXXX.rs 1:1]
|
|
||||||
input i_0_to_1: Ty0 @[module-XXXXXXXXXX.rs 2:1]
|
|
||||||
input i_0_to_2: Ty1 @[module-XXXXXXXXXX.rs 3:1]
|
|
||||||
input i_0_to_3: Ty2 @[module-XXXXXXXXXX.rs 4:1]
|
|
||||||
input i_0_to_4: Ty3 @[module-XXXXXXXXXX.rs 5:1]
|
|
||||||
input i_0_to_7: Ty4 @[module-XXXXXXXXXX.rs 6:1]
|
|
||||||
input i_0_to_8: Ty5 @[module-XXXXXXXXXX.rs 7:1]
|
|
||||||
input i_0_to_9: Ty6 @[module-XXXXXXXXXX.rs 8:1]
|
|
||||||
input i_0_through_0: Ty7 @[module-XXXXXXXXXX.rs 9:1]
|
|
||||||
input i_0_through_1: Ty8 @[module-XXXXXXXXXX.rs 10:1]
|
|
||||||
input i_0_through_2: Ty9 @[module-XXXXXXXXXX.rs 11:1]
|
|
||||||
input i_0_through_3: Ty10 @[module-XXXXXXXXXX.rs 12:1]
|
|
||||||
input i_0_through_4: Ty11 @[module-XXXXXXXXXX.rs 13:1]
|
|
||||||
input i_0_through_7: Ty12 @[module-XXXXXXXXXX.rs 14:1]
|
|
||||||
input i_0_through_8: Ty13 @[module-XXXXXXXXXX.rs 15:1]
|
|
||||||
input i_0_through_9: Ty14 @[module-XXXXXXXXXX.rs 16:1]
|
|
||||||
",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -419,7 +419,6 @@ Simulation {
|
||||||
},
|
},
|
||||||
pc: 38,
|
pc: 38,
|
||||||
memory_write_log: [],
|
memory_write_log: [],
|
||||||
assert_failed_log: [],
|
|
||||||
memories: StatePart {
|
memories: StatePart {
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
|
|
@ -498,7 +497,6 @@ Simulation {
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
global_io: {},
|
|
||||||
main_module: SimulationModuleState {
|
main_module: SimulationModuleState {
|
||||||
base_targets: [
|
base_targets: [
|
||||||
Instance {
|
Instance {
|
||||||
|
|
@ -828,9 +826,9 @@ Simulation {
|
||||||
}.write_index,
|
}.write_index,
|
||||||
},
|
},
|
||||||
did_initial_settle: true,
|
did_initial_settle: true,
|
||||||
clocks_for_past: {},
|
|
||||||
},
|
},
|
||||||
extern_modules: [],
|
extern_modules: [],
|
||||||
|
state_ready_to_run: false,
|
||||||
trace_decls: TraceModule {
|
trace_decls: TraceModule {
|
||||||
name: "array_rw",
|
name: "array_rw",
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -1220,7 +1218,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(0),
|
index: StatePartIndex<BigSlots>(0),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xff,
|
state: 0xff,
|
||||||
last_state: 0xff,
|
last_state: 0xff,
|
||||||
},
|
},
|
||||||
|
|
@ -1230,7 +1227,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(1),
|
index: StatePartIndex<BigSlots>(1),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x7f,
|
state: 0x7f,
|
||||||
last_state: 0x7f,
|
last_state: 0x7f,
|
||||||
},
|
},
|
||||||
|
|
@ -1240,7 +1236,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(2),
|
index: StatePartIndex<BigSlots>(2),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x3f,
|
state: 0x3f,
|
||||||
last_state: 0x3f,
|
last_state: 0x3f,
|
||||||
},
|
},
|
||||||
|
|
@ -1250,7 +1245,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(3),
|
index: StatePartIndex<BigSlots>(3),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x1f,
|
state: 0x1f,
|
||||||
last_state: 0x1f,
|
last_state: 0x1f,
|
||||||
},
|
},
|
||||||
|
|
@ -1260,7 +1254,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(4),
|
index: StatePartIndex<BigSlots>(4),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x0f,
|
state: 0x0f,
|
||||||
last_state: 0x0f,
|
last_state: 0x0f,
|
||||||
},
|
},
|
||||||
|
|
@ -1270,7 +1263,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(5),
|
index: StatePartIndex<BigSlots>(5),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x07,
|
state: 0x07,
|
||||||
last_state: 0x07,
|
last_state: 0x07,
|
||||||
},
|
},
|
||||||
|
|
@ -1280,7 +1272,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(6),
|
index: StatePartIndex<BigSlots>(6),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x03,
|
state: 0x03,
|
||||||
last_state: 0x03,
|
last_state: 0x03,
|
||||||
},
|
},
|
||||||
|
|
@ -1290,7 +1281,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(7),
|
index: StatePartIndex<BigSlots>(7),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x01,
|
state: 0x01,
|
||||||
last_state: 0x01,
|
last_state: 0x01,
|
||||||
},
|
},
|
||||||
|
|
@ -1300,7 +1290,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(8),
|
index: StatePartIndex<BigSlots>(8),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x00,
|
state: 0x00,
|
||||||
last_state: 0x00,
|
last_state: 0x00,
|
||||||
},
|
},
|
||||||
|
|
@ -1310,7 +1299,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(9),
|
index: StatePartIndex<BigSlots>(9),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x80,
|
state: 0x80,
|
||||||
last_state: 0x80,
|
last_state: 0x80,
|
||||||
},
|
},
|
||||||
|
|
@ -1320,7 +1308,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(10),
|
index: StatePartIndex<BigSlots>(10),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xc0,
|
state: 0xc0,
|
||||||
last_state: 0xc0,
|
last_state: 0xc0,
|
||||||
},
|
},
|
||||||
|
|
@ -1330,7 +1317,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(11),
|
index: StatePartIndex<BigSlots>(11),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xe0,
|
state: 0xe0,
|
||||||
last_state: 0xe0,
|
last_state: 0xe0,
|
||||||
},
|
},
|
||||||
|
|
@ -1340,7 +1326,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(12),
|
index: StatePartIndex<BigSlots>(12),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xf0,
|
state: 0xf0,
|
||||||
last_state: 0xf0,
|
last_state: 0xf0,
|
||||||
},
|
},
|
||||||
|
|
@ -1350,7 +1335,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(13),
|
index: StatePartIndex<BigSlots>(13),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xf8,
|
state: 0xf8,
|
||||||
last_state: 0xf8,
|
last_state: 0xf8,
|
||||||
},
|
},
|
||||||
|
|
@ -1360,7 +1344,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(14),
|
index: StatePartIndex<BigSlots>(14),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xfc,
|
state: 0xfc,
|
||||||
last_state: 0xfc,
|
last_state: 0xfc,
|
||||||
},
|
},
|
||||||
|
|
@ -1370,7 +1353,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(15),
|
index: StatePartIndex<BigSlots>(15),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xfe,
|
state: 0xfe,
|
||||||
last_state: 0xfe,
|
last_state: 0xfe,
|
||||||
},
|
},
|
||||||
|
|
@ -1380,7 +1362,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(16),
|
index: StatePartIndex<BigSlots>(16),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xff,
|
state: 0xff,
|
||||||
last_state: 0xff,
|
last_state: 0xff,
|
||||||
},
|
},
|
||||||
|
|
@ -1390,7 +1371,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(17),
|
index: StatePartIndex<BigSlots>(17),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x7f,
|
state: 0x7f,
|
||||||
last_state: 0x7f,
|
last_state: 0x7f,
|
||||||
},
|
},
|
||||||
|
|
@ -1400,7 +1380,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(18),
|
index: StatePartIndex<BigSlots>(18),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x3f,
|
state: 0x3f,
|
||||||
last_state: 0x3f,
|
last_state: 0x3f,
|
||||||
},
|
},
|
||||||
|
|
@ -1410,7 +1389,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(19),
|
index: StatePartIndex<BigSlots>(19),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x1f,
|
state: 0x1f,
|
||||||
last_state: 0x1f,
|
last_state: 0x1f,
|
||||||
},
|
},
|
||||||
|
|
@ -1420,7 +1398,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(20),
|
index: StatePartIndex<BigSlots>(20),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x0f,
|
state: 0x0f,
|
||||||
last_state: 0x0f,
|
last_state: 0x0f,
|
||||||
},
|
},
|
||||||
|
|
@ -1430,7 +1407,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(21),
|
index: StatePartIndex<BigSlots>(21),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x07,
|
state: 0x07,
|
||||||
last_state: 0x07,
|
last_state: 0x07,
|
||||||
},
|
},
|
||||||
|
|
@ -1440,7 +1416,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(22),
|
index: StatePartIndex<BigSlots>(22),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x03,
|
state: 0x03,
|
||||||
last_state: 0x03,
|
last_state: 0x03,
|
||||||
},
|
},
|
||||||
|
|
@ -1450,7 +1425,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(23),
|
index: StatePartIndex<BigSlots>(23),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x01,
|
state: 0x01,
|
||||||
last_state: 0x01,
|
last_state: 0x01,
|
||||||
},
|
},
|
||||||
|
|
@ -1460,7 +1434,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(24),
|
index: StatePartIndex<BigSlots>(24),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x00,
|
state: 0x00,
|
||||||
last_state: 0x00,
|
last_state: 0x00,
|
||||||
},
|
},
|
||||||
|
|
@ -1470,7 +1443,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(25),
|
index: StatePartIndex<BigSlots>(25),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x80,
|
state: 0x80,
|
||||||
last_state: 0x80,
|
last_state: 0x80,
|
||||||
},
|
},
|
||||||
|
|
@ -1480,7 +1452,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(26),
|
index: StatePartIndex<BigSlots>(26),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xc0,
|
state: 0xc0,
|
||||||
last_state: 0xc0,
|
last_state: 0xc0,
|
||||||
},
|
},
|
||||||
|
|
@ -1490,7 +1461,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(27),
|
index: StatePartIndex<BigSlots>(27),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xe0,
|
state: 0xe0,
|
||||||
last_state: 0xe0,
|
last_state: 0xe0,
|
||||||
},
|
},
|
||||||
|
|
@ -1500,7 +1470,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(28),
|
index: StatePartIndex<BigSlots>(28),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xf0,
|
state: 0xf0,
|
||||||
last_state: 0xf0,
|
last_state: 0xf0,
|
||||||
},
|
},
|
||||||
|
|
@ -1510,7 +1479,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(29),
|
index: StatePartIndex<BigSlots>(29),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xf8,
|
state: 0xf8,
|
||||||
last_state: 0xf8,
|
last_state: 0xf8,
|
||||||
},
|
},
|
||||||
|
|
@ -1520,7 +1488,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(30),
|
index: StatePartIndex<BigSlots>(30),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xfc,
|
state: 0xfc,
|
||||||
last_state: 0xfc,
|
last_state: 0xfc,
|
||||||
},
|
},
|
||||||
|
|
@ -1530,7 +1497,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(31),
|
index: StatePartIndex<BigSlots>(31),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xfe,
|
state: 0xfe,
|
||||||
last_state: 0xe1,
|
last_state: 0xe1,
|
||||||
},
|
},
|
||||||
|
|
@ -1540,7 +1506,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(32),
|
index: StatePartIndex<BigSlots>(32),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x00,
|
state: 0x00,
|
||||||
last_state: 0x00,
|
last_state: 0x00,
|
||||||
},
|
},
|
||||||
|
|
@ -1550,7 +1515,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(33),
|
index: StatePartIndex<BigSlots>(33),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xff,
|
state: 0xff,
|
||||||
last_state: 0xff,
|
last_state: 0xff,
|
||||||
},
|
},
|
||||||
|
|
@ -1560,7 +1524,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(34),
|
index: StatePartIndex<BigSlots>(34),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x10,
|
state: 0x10,
|
||||||
last_state: 0x0f,
|
last_state: 0x0f,
|
||||||
},
|
},
|
||||||
|
|
@ -1570,7 +1533,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(35),
|
index: StatePartIndex<BigSlots>(35),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x00,
|
state: 0x00,
|
||||||
last_state: 0xe1,
|
last_state: 0xe1,
|
||||||
},
|
},
|
||||||
|
|
@ -1579,7 +1541,6 @@ Simulation {
|
||||||
kind: BigBool {
|
kind: BigBool {
|
||||||
index: StatePartIndex<BigSlots>(36),
|
index: StatePartIndex<BigSlots>(36),
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x1,
|
state: 0x1,
|
||||||
last_state: 0x1,
|
last_state: 0x1,
|
||||||
},
|
},
|
||||||
|
|
@ -1589,7 +1550,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(37),
|
index: StatePartIndex<BigSlots>(37),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xff,
|
state: 0xff,
|
||||||
last_state: 0xff,
|
last_state: 0xff,
|
||||||
},
|
},
|
||||||
|
|
@ -1599,7 +1559,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(38),
|
index: StatePartIndex<BigSlots>(38),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x7f,
|
state: 0x7f,
|
||||||
last_state: 0x7f,
|
last_state: 0x7f,
|
||||||
},
|
},
|
||||||
|
|
@ -1609,7 +1568,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(39),
|
index: StatePartIndex<BigSlots>(39),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x3f,
|
state: 0x3f,
|
||||||
last_state: 0x3f,
|
last_state: 0x3f,
|
||||||
},
|
},
|
||||||
|
|
@ -1619,7 +1577,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(40),
|
index: StatePartIndex<BigSlots>(40),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x1f,
|
state: 0x1f,
|
||||||
last_state: 0x1f,
|
last_state: 0x1f,
|
||||||
},
|
},
|
||||||
|
|
@ -1629,7 +1586,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(41),
|
index: StatePartIndex<BigSlots>(41),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x0f,
|
state: 0x0f,
|
||||||
last_state: 0x0f,
|
last_state: 0x0f,
|
||||||
},
|
},
|
||||||
|
|
@ -1639,7 +1595,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(42),
|
index: StatePartIndex<BigSlots>(42),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x07,
|
state: 0x07,
|
||||||
last_state: 0x07,
|
last_state: 0x07,
|
||||||
},
|
},
|
||||||
|
|
@ -1649,7 +1604,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(43),
|
index: StatePartIndex<BigSlots>(43),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x03,
|
state: 0x03,
|
||||||
last_state: 0x03,
|
last_state: 0x03,
|
||||||
},
|
},
|
||||||
|
|
@ -1659,7 +1613,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(44),
|
index: StatePartIndex<BigSlots>(44),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x01,
|
state: 0x01,
|
||||||
last_state: 0x01,
|
last_state: 0x01,
|
||||||
},
|
},
|
||||||
|
|
@ -1669,7 +1622,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(45),
|
index: StatePartIndex<BigSlots>(45),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x00,
|
state: 0x00,
|
||||||
last_state: 0x00,
|
last_state: 0x00,
|
||||||
},
|
},
|
||||||
|
|
@ -1679,7 +1631,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(46),
|
index: StatePartIndex<BigSlots>(46),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x80,
|
state: 0x80,
|
||||||
last_state: 0x80,
|
last_state: 0x80,
|
||||||
},
|
},
|
||||||
|
|
@ -1689,7 +1640,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(47),
|
index: StatePartIndex<BigSlots>(47),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xc0,
|
state: 0xc0,
|
||||||
last_state: 0xc0,
|
last_state: 0xc0,
|
||||||
},
|
},
|
||||||
|
|
@ -1699,7 +1649,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(48),
|
index: StatePartIndex<BigSlots>(48),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xe0,
|
state: 0xe0,
|
||||||
last_state: 0xe0,
|
last_state: 0xe0,
|
||||||
},
|
},
|
||||||
|
|
@ -1709,7 +1658,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(49),
|
index: StatePartIndex<BigSlots>(49),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xf0,
|
state: 0xf0,
|
||||||
last_state: 0xf0,
|
last_state: 0xf0,
|
||||||
},
|
},
|
||||||
|
|
@ -1719,7 +1667,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(50),
|
index: StatePartIndex<BigSlots>(50),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xf8,
|
state: 0xf8,
|
||||||
last_state: 0xf8,
|
last_state: 0xf8,
|
||||||
},
|
},
|
||||||
|
|
@ -1729,7 +1676,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(51),
|
index: StatePartIndex<BigSlots>(51),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xfc,
|
state: 0xfc,
|
||||||
last_state: 0xfc,
|
last_state: 0xfc,
|
||||||
},
|
},
|
||||||
|
|
@ -1739,7 +1685,6 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(52),
|
index: StatePartIndex<BigSlots>(52),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0xfe,
|
state: 0xfe,
|
||||||
last_state: 0xe1,
|
last_state: 0xe1,
|
||||||
},
|
},
|
||||||
|
|
@ -1754,13 +1699,7 @@ Simulation {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
clocks_triggered: [],
|
|
||||||
event_queue: EventQueue(EventQueueData {
|
|
||||||
instant: 34 μs,
|
instant: 34 μs,
|
||||||
events: {},
|
clocks_triggered: [],
|
||||||
}),
|
|
||||||
waiting_sensitivity_sets_by_address: {},
|
|
||||||
waiting_sensitivity_sets_by_compiled_value: {},
|
|
||||||
asserts: [],
|
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
|
@ -1,283 +1,283 @@
|
||||||
$timescale 1 ps $end
|
$timescale 1 ps $end
|
||||||
$scope module array_rw $end
|
$scope module array_rw $end
|
||||||
$scope struct array_in $end
|
$scope struct array_in $end
|
||||||
$var wire 8 Yvfu^ \[0] $end
|
$var wire 8 ! \[0] $end
|
||||||
$var wire 8 |Cs`W \[1] $end
|
$var wire 8 " \[1] $end
|
||||||
$var wire 8 M!nsb \[2] $end
|
$var wire 8 # \[2] $end
|
||||||
$var wire 8 59L{w \[3] $end
|
$var wire 8 $ \[3] $end
|
||||||
$var wire 8 o2+|F \[4] $end
|
$var wire 8 % \[4] $end
|
||||||
$var wire 8 ikzV5 \[5] $end
|
$var wire 8 & \[5] $end
|
||||||
$var wire 8 [E$Z* \[6] $end
|
$var wire 8 ' \[6] $end
|
||||||
$var wire 8 ?"~01 \[7] $end
|
$var wire 8 ( \[7] $end
|
||||||
$var wire 8 /kghT \[8] $end
|
$var wire 8 ) \[8] $end
|
||||||
$var wire 8 +}(9) \[9] $end
|
$var wire 8 * \[9] $end
|
||||||
$var wire 8 iMP}= \[10] $end
|
$var wire 8 + \[10] $end
|
||||||
$var wire 8 2M0tL \[11] $end
|
$var wire 8 , \[11] $end
|
||||||
$var wire 8 :AjkA \[12] $end
|
$var wire 8 - \[12] $end
|
||||||
$var wire 8 VM_:8 \[13] $end
|
$var wire 8 . \[13] $end
|
||||||
$var wire 8 UveL2 \[14] $end
|
$var wire 8 / \[14] $end
|
||||||
$var wire 8 A)9Z6 \[15] $end
|
$var wire 8 0 \[15] $end
|
||||||
$upscope $end
|
$upscope $end
|
||||||
$scope struct array_out $end
|
$scope struct array_out $end
|
||||||
$var wire 8 2zdj1 \[0] $end
|
$var wire 8 1 \[0] $end
|
||||||
$var wire 8 =;m_[ \[1] $end
|
$var wire 8 2 \[1] $end
|
||||||
$var wire 8 @9Hd \[2] $end
|
$var wire 8 3 \[2] $end
|
||||||
$var wire 8 C:="| \[3] $end
|
$var wire 8 4 \[3] $end
|
||||||
$var wire 8 IDk7# \[4] $end
|
$var wire 8 5 \[4] $end
|
||||||
$var wire 8 i]E1i \[5] $end
|
$var wire 8 6 \[5] $end
|
||||||
$var wire 8 tK,M] \[6] $end
|
$var wire 8 7 \[6] $end
|
||||||
$var wire 8 tGp!\ \[7] $end
|
$var wire 8 8 \[7] $end
|
||||||
$var wire 8 ."qjK \[8] $end
|
$var wire 8 9 \[8] $end
|
||||||
$var wire 8 AUO:R \[9] $end
|
$var wire 8 : \[9] $end
|
||||||
$var wire 8 'kx`n \[10] $end
|
$var wire 8 ; \[10] $end
|
||||||
$var wire 8 U&(K\ \[11] $end
|
$var wire 8 < \[11] $end
|
||||||
$var wire 8 q<O41 \[12] $end
|
$var wire 8 = \[12] $end
|
||||||
$var wire 8 zvj)] \[13] $end
|
$var wire 8 > \[13] $end
|
||||||
$var wire 8 >0H<( \[14] $end
|
$var wire 8 ? \[14] $end
|
||||||
$var wire 8 ARhXJ \[15] $end
|
$var wire 8 @ \[15] $end
|
||||||
$upscope $end
|
$upscope $end
|
||||||
$var wire 8 -n:7@ read_index $end
|
$var wire 8 A read_index $end
|
||||||
$var wire 8 >h<=Z read_data $end
|
$var wire 8 B read_data $end
|
||||||
$var wire 8 [xld3 write_index $end
|
$var wire 8 C write_index $end
|
||||||
$var wire 8 J+DYh write_data $end
|
$var wire 8 D write_data $end
|
||||||
$var wire 1 z,@WW write_en $end
|
$var wire 1 E write_en $end
|
||||||
$scope struct array_wire $end
|
$scope struct array_wire $end
|
||||||
$var wire 8 B{KJS \[0] $end
|
$var wire 8 F \[0] $end
|
||||||
$var wire 8 V'K*& \[1] $end
|
$var wire 8 G \[1] $end
|
||||||
$var wire 8 4zI$O \[2] $end
|
$var wire 8 H \[2] $end
|
||||||
$var wire 8 %TTk[ \[3] $end
|
$var wire 8 I \[3] $end
|
||||||
$var wire 8 IgSeY \[4] $end
|
$var wire 8 J \[4] $end
|
||||||
$var wire 8 &&1T" \[5] $end
|
$var wire 8 K \[5] $end
|
||||||
$var wire 8 5)-l\ \[6] $end
|
$var wire 8 L \[6] $end
|
||||||
$var wire 8 0RsLb \[7] $end
|
$var wire 8 M \[7] $end
|
||||||
$var wire 8 T>:}D \[8] $end
|
$var wire 8 N \[8] $end
|
||||||
$var wire 8 DPpZ* \[9] $end
|
$var wire 8 O \[9] $end
|
||||||
$var wire 8 %E(nf \[10] $end
|
$var wire 8 P \[10] $end
|
||||||
$var wire 8 2'pba \[11] $end
|
$var wire 8 Q \[11] $end
|
||||||
$var wire 8 e/c1: \[12] $end
|
$var wire 8 R \[12] $end
|
||||||
$var wire 8 ;w.C7 \[13] $end
|
$var wire 8 S \[13] $end
|
||||||
$var wire 8 fwdfu \[14] $end
|
$var wire 8 T \[14] $end
|
||||||
$var wire 8 *R\vx \[15] $end
|
$var wire 8 U \[15] $end
|
||||||
$upscope $end
|
$upscope $end
|
||||||
$upscope $end
|
$upscope $end
|
||||||
$enddefinitions $end
|
$enddefinitions $end
|
||||||
$dumpvars
|
$dumpvars
|
||||||
b11111111 Yvfu^
|
b11111111 !
|
||||||
b1111111 |Cs`W
|
b1111111 "
|
||||||
b111111 M!nsb
|
b111111 #
|
||||||
b11111 59L{w
|
b11111 $
|
||||||
b1111 o2+|F
|
b1111 %
|
||||||
b111 ikzV5
|
b111 &
|
||||||
b11 [E$Z*
|
b11 '
|
||||||
b1 ?"~01
|
b1 (
|
||||||
b0 /kghT
|
b0 )
|
||||||
b10000000 +}(9)
|
b10000000 *
|
||||||
b11000000 iMP}=
|
b11000000 +
|
||||||
b11100000 2M0tL
|
b11100000 ,
|
||||||
b11110000 :AjkA
|
b11110000 -
|
||||||
b11111000 VM_:8
|
b11111000 .
|
||||||
b11111100 UveL2
|
b11111100 /
|
||||||
b11111110 A)9Z6
|
b11111110 0
|
||||||
b11111111 2zdj1
|
b11111111 1
|
||||||
b1111111 =;m_[
|
b1111111 2
|
||||||
b111111 @9Hd
|
b111111 3
|
||||||
b11111 C:="|
|
b11111 4
|
||||||
b1111 IDk7#
|
b1111 5
|
||||||
b111 i]E1i
|
b111 6
|
||||||
b11 tK,M]
|
b11 7
|
||||||
b1 tGp!\
|
b1 8
|
||||||
b0 ."qjK
|
b0 9
|
||||||
b10000000 AUO:R
|
b10000000 :
|
||||||
b11000000 'kx`n
|
b11000000 ;
|
||||||
b11100000 U&(K\
|
b11100000 <
|
||||||
b11110000 q<O41
|
b11110000 =
|
||||||
b11111000 zvj)]
|
b11111000 >
|
||||||
b11111100 >0H<(
|
b11111100 ?
|
||||||
b11111110 ARhXJ
|
b11111110 @
|
||||||
b0 -n:7@
|
b0 A
|
||||||
b11111111 >h<=Z
|
b11111111 B
|
||||||
b0 [xld3
|
b0 C
|
||||||
b0 J+DYh
|
b0 D
|
||||||
0z,@WW
|
0E
|
||||||
b11111111 B{KJS
|
b11111111 F
|
||||||
b1111111 V'K*&
|
b1111111 G
|
||||||
b111111 4zI$O
|
b111111 H
|
||||||
b11111 %TTk[
|
b11111 I
|
||||||
b1111 IgSeY
|
b1111 J
|
||||||
b111 &&1T"
|
b111 K
|
||||||
b11 5)-l\
|
b11 L
|
||||||
b1 0RsLb
|
b1 M
|
||||||
b0 T>:}D
|
b0 N
|
||||||
b10000000 DPpZ*
|
b10000000 O
|
||||||
b11000000 %E(nf
|
b11000000 P
|
||||||
b11100000 2'pba
|
b11100000 Q
|
||||||
b11110000 e/c1:
|
b11110000 R
|
||||||
b11111000 ;w.C7
|
b11111000 S
|
||||||
b11111100 fwdfu
|
b11111100 T
|
||||||
b11111110 *R\vx
|
b11111110 U
|
||||||
$end
|
$end
|
||||||
#1000000
|
#1000000
|
||||||
b1 -n:7@
|
b1 A
|
||||||
b1111111 >h<=Z
|
b1111111 B
|
||||||
#2000000
|
#2000000
|
||||||
b10 -n:7@
|
b10 A
|
||||||
b111111 >h<=Z
|
b111111 B
|
||||||
#3000000
|
#3000000
|
||||||
b11 -n:7@
|
b11 A
|
||||||
b11111 >h<=Z
|
b11111 B
|
||||||
#4000000
|
#4000000
|
||||||
b100 -n:7@
|
b100 A
|
||||||
b1111 >h<=Z
|
b1111 B
|
||||||
#5000000
|
#5000000
|
||||||
b101 -n:7@
|
b101 A
|
||||||
b111 >h<=Z
|
b111 B
|
||||||
#6000000
|
#6000000
|
||||||
b110 -n:7@
|
b110 A
|
||||||
b11 >h<=Z
|
b11 B
|
||||||
#7000000
|
#7000000
|
||||||
b111 -n:7@
|
b111 A
|
||||||
b1 >h<=Z
|
b1 B
|
||||||
#8000000
|
#8000000
|
||||||
b1000 -n:7@
|
b1000 A
|
||||||
b0 >h<=Z
|
b0 B
|
||||||
#9000000
|
#9000000
|
||||||
b1001 -n:7@
|
b1001 A
|
||||||
b10000000 >h<=Z
|
b10000000 B
|
||||||
#10000000
|
#10000000
|
||||||
b1010 -n:7@
|
b1010 A
|
||||||
b11000000 >h<=Z
|
b11000000 B
|
||||||
#11000000
|
#11000000
|
||||||
b1011 -n:7@
|
b1011 A
|
||||||
b11100000 >h<=Z
|
b11100000 B
|
||||||
#12000000
|
#12000000
|
||||||
b1100 -n:7@
|
b1100 A
|
||||||
b11110000 >h<=Z
|
b11110000 B
|
||||||
#13000000
|
#13000000
|
||||||
b1101 -n:7@
|
b1101 A
|
||||||
b11111000 >h<=Z
|
b11111000 B
|
||||||
#14000000
|
#14000000
|
||||||
b1110 -n:7@
|
b1110 A
|
||||||
b11111100 >h<=Z
|
b11111100 B
|
||||||
#15000000
|
#15000000
|
||||||
b1111 -n:7@
|
b1111 A
|
||||||
b11111110 >h<=Z
|
b11111110 B
|
||||||
#16000000
|
#16000000
|
||||||
b10000 -n:7@
|
b10000 A
|
||||||
b0 >h<=Z
|
b0 B
|
||||||
#17000000
|
#17000000
|
||||||
b0 2zdj1
|
b0 1
|
||||||
b0 -n:7@
|
b0 A
|
||||||
1z,@WW
|
1E
|
||||||
b0 B{KJS
|
b0 F
|
||||||
#18000000
|
#18000000
|
||||||
b11111111 2zdj1
|
b11111111 1
|
||||||
b1 =;m_[
|
b1 2
|
||||||
b11111111 >h<=Z
|
b11111111 B
|
||||||
b1 [xld3
|
b1 C
|
||||||
b1 J+DYh
|
b1 D
|
||||||
b11111111 B{KJS
|
b11111111 F
|
||||||
b1 V'K*&
|
b1 G
|
||||||
#19000000
|
#19000000
|
||||||
b1111111 =;m_[
|
b1111111 2
|
||||||
b100 @9Hd
|
b100 3
|
||||||
b10 [xld3
|
b10 C
|
||||||
b100 J+DYh
|
b100 D
|
||||||
b1111111 V'K*&
|
b1111111 G
|
||||||
b100 4zI$O
|
b100 H
|
||||||
#20000000
|
#20000000
|
||||||
b111111 @9Hd
|
b111111 3
|
||||||
b1001 C:="|
|
b1001 4
|
||||||
b11 [xld3
|
b11 C
|
||||||
b1001 J+DYh
|
b1001 D
|
||||||
b111111 4zI$O
|
b111111 H
|
||||||
b1001 %TTk[
|
b1001 I
|
||||||
#21000000
|
#21000000
|
||||||
b11111 C:="|
|
b11111 4
|
||||||
b10000 IDk7#
|
b10000 5
|
||||||
b100 [xld3
|
b100 C
|
||||||
b10000 J+DYh
|
b10000 D
|
||||||
b11111 %TTk[
|
b11111 I
|
||||||
b10000 IgSeY
|
b10000 J
|
||||||
#22000000
|
#22000000
|
||||||
b1111 IDk7#
|
b1111 5
|
||||||
b11001 i]E1i
|
b11001 6
|
||||||
b101 [xld3
|
b101 C
|
||||||
b11001 J+DYh
|
b11001 D
|
||||||
b1111 IgSeY
|
b1111 J
|
||||||
b11001 &&1T"
|
b11001 K
|
||||||
#23000000
|
#23000000
|
||||||
b111 i]E1i
|
b111 6
|
||||||
b100100 tK,M]
|
b100100 7
|
||||||
b110 [xld3
|
b110 C
|
||||||
b100100 J+DYh
|
b100100 D
|
||||||
b111 &&1T"
|
b111 K
|
||||||
b100100 5)-l\
|
b100100 L
|
||||||
#24000000
|
#24000000
|
||||||
b11 tK,M]
|
b11 7
|
||||||
b110001 tGp!\
|
b110001 8
|
||||||
b111 [xld3
|
b111 C
|
||||||
b110001 J+DYh
|
b110001 D
|
||||||
b11 5)-l\
|
b11 L
|
||||||
b110001 0RsLb
|
b110001 M
|
||||||
#25000000
|
#25000000
|
||||||
b1 tGp!\
|
b1 8
|
||||||
b1000000 ."qjK
|
b1000000 9
|
||||||
b1000 [xld3
|
b1000 C
|
||||||
b1000000 J+DYh
|
b1000000 D
|
||||||
b1 0RsLb
|
b1 M
|
||||||
b1000000 T>:}D
|
b1000000 N
|
||||||
#26000000
|
#26000000
|
||||||
b0 ."qjK
|
b0 9
|
||||||
b1010001 AUO:R
|
b1010001 :
|
||||||
b1001 [xld3
|
b1001 C
|
||||||
b1010001 J+DYh
|
b1010001 D
|
||||||
b0 T>:}D
|
b0 N
|
||||||
b1010001 DPpZ*
|
b1010001 O
|
||||||
#27000000
|
#27000000
|
||||||
b10000000 AUO:R
|
b10000000 :
|
||||||
b1100100 'kx`n
|
b1100100 ;
|
||||||
b1010 [xld3
|
b1010 C
|
||||||
b1100100 J+DYh
|
b1100100 D
|
||||||
b10000000 DPpZ*
|
b10000000 O
|
||||||
b1100100 %E(nf
|
b1100100 P
|
||||||
#28000000
|
#28000000
|
||||||
b11000000 'kx`n
|
b11000000 ;
|
||||||
b1111001 U&(K\
|
b1111001 <
|
||||||
b1011 [xld3
|
b1011 C
|
||||||
b1111001 J+DYh
|
b1111001 D
|
||||||
b11000000 %E(nf
|
b11000000 P
|
||||||
b1111001 2'pba
|
b1111001 Q
|
||||||
#29000000
|
#29000000
|
||||||
b11100000 U&(K\
|
b11100000 <
|
||||||
b10010000 q<O41
|
b10010000 =
|
||||||
b1100 [xld3
|
b1100 C
|
||||||
b10010000 J+DYh
|
b10010000 D
|
||||||
b11100000 2'pba
|
b11100000 Q
|
||||||
b10010000 e/c1:
|
b10010000 R
|
||||||
#30000000
|
#30000000
|
||||||
b11110000 q<O41
|
b11110000 =
|
||||||
b10101001 zvj)]
|
b10101001 >
|
||||||
b1101 [xld3
|
b1101 C
|
||||||
b10101001 J+DYh
|
b10101001 D
|
||||||
b11110000 e/c1:
|
b11110000 R
|
||||||
b10101001 ;w.C7
|
b10101001 S
|
||||||
#31000000
|
#31000000
|
||||||
b11111000 zvj)]
|
b11111000 >
|
||||||
b11000100 >0H<(
|
b11000100 ?
|
||||||
b1110 [xld3
|
b1110 C
|
||||||
b11000100 J+DYh
|
b11000100 D
|
||||||
b11111000 ;w.C7
|
b11111000 S
|
||||||
b11000100 fwdfu
|
b11000100 T
|
||||||
#32000000
|
#32000000
|
||||||
b11111100 >0H<(
|
b11111100 ?
|
||||||
b11100001 ARhXJ
|
b11100001 @
|
||||||
b1111 [xld3
|
b1111 C
|
||||||
b11100001 J+DYh
|
b11100001 D
|
||||||
b11111100 fwdfu
|
b11111100 T
|
||||||
b11100001 *R\vx
|
b11100001 U
|
||||||
#33000000
|
#33000000
|
||||||
b11111110 ARhXJ
|
b11111110 @
|
||||||
b10000 [xld3
|
b10000 C
|
||||||
b0 J+DYh
|
b0 D
|
||||||
b11111110 *R\vx
|
b11111110 U
|
||||||
#34000000
|
#34000000
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@ Simulation {
|
||||||
},
|
},
|
||||||
pc: 5,
|
pc: 5,
|
||||||
memory_write_log: [],
|
memory_write_log: [],
|
||||||
assert_failed_log: [],
|
|
||||||
memories: StatePart {
|
memories: StatePart {
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
|
|
@ -102,7 +101,6 @@ Simulation {
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
global_io: {},
|
|
||||||
main_module: SimulationModuleState {
|
main_module: SimulationModuleState {
|
||||||
base_targets: [
|
base_targets: [
|
||||||
Instance {
|
Instance {
|
||||||
|
|
@ -124,9 +122,9 @@ Simulation {
|
||||||
}.i,
|
}.i,
|
||||||
},
|
},
|
||||||
did_initial_settle: true,
|
did_initial_settle: true,
|
||||||
clocks_for_past: {},
|
|
||||||
},
|
},
|
||||||
extern_modules: [],
|
extern_modules: [],
|
||||||
|
state_ready_to_run: false,
|
||||||
trace_decls: TraceModule {
|
trace_decls: TraceModule {
|
||||||
name: "conditional_assignment_last",
|
name: "conditional_assignment_last",
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -157,7 +155,6 @@ Simulation {
|
||||||
kind: BigBool {
|
kind: BigBool {
|
||||||
index: StatePartIndex<BigSlots>(0),
|
index: StatePartIndex<BigSlots>(0),
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x1,
|
state: 0x1,
|
||||||
last_state: 0x0,
|
last_state: 0x0,
|
||||||
},
|
},
|
||||||
|
|
@ -166,7 +163,6 @@ Simulation {
|
||||||
kind: BigBool {
|
kind: BigBool {
|
||||||
index: StatePartIndex<BigSlots>(1),
|
index: StatePartIndex<BigSlots>(1),
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x0,
|
state: 0x0,
|
||||||
last_state: 0x1,
|
last_state: 0x1,
|
||||||
},
|
},
|
||||||
|
|
@ -181,13 +177,7 @@ Simulation {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
clocks_triggered: [],
|
|
||||||
event_queue: EventQueue(EventQueueData {
|
|
||||||
instant: 2 μs,
|
instant: 2 μs,
|
||||||
events: {},
|
clocks_triggered: [],
|
||||||
}),
|
|
||||||
waiting_sensitivity_sets_by_address: {},
|
|
||||||
waiting_sensitivity_sets_by_compiled_value: {},
|
|
||||||
asserts: [],
|
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
$timescale 1 ps $end
|
$timescale 1 ps $end
|
||||||
$scope module conditional_assignment_last $end
|
$scope module conditional_assignment_last $end
|
||||||
$var wire 1 xt~(W i $end
|
$var wire 1 ! i $end
|
||||||
$var wire 1 6:7im w $end
|
$var wire 1 " w $end
|
||||||
$upscope $end
|
$upscope $end
|
||||||
$enddefinitions $end
|
$enddefinitions $end
|
||||||
$dumpvars
|
$dumpvars
|
||||||
0xt~(W
|
0!
|
||||||
16:7im
|
1"
|
||||||
$end
|
$end
|
||||||
#1000000
|
#1000000
|
||||||
1xt~(W
|
1!
|
||||||
06:7im
|
0"
|
||||||
#2000000
|
#2000000
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ Simulation {
|
||||||
},
|
},
|
||||||
pc: 2,
|
pc: 2,
|
||||||
memory_write_log: [],
|
memory_write_log: [],
|
||||||
assert_failed_log: [],
|
|
||||||
memories: StatePart {
|
memories: StatePart {
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
|
|
@ -78,7 +77,6 @@ Simulation {
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
global_io: {},
|
|
||||||
main_module: SimulationModuleState {
|
main_module: SimulationModuleState {
|
||||||
base_targets: [
|
base_targets: [
|
||||||
Instance {
|
Instance {
|
||||||
|
|
@ -100,9 +98,9 @@ Simulation {
|
||||||
}.o,
|
}.o,
|
||||||
},
|
},
|
||||||
did_initial_settle: true,
|
did_initial_settle: true,
|
||||||
clocks_for_past: {},
|
|
||||||
},
|
},
|
||||||
extern_modules: [],
|
extern_modules: [],
|
||||||
|
state_ready_to_run: false,
|
||||||
trace_decls: TraceModule {
|
trace_decls: TraceModule {
|
||||||
name: "connect_const",
|
name: "connect_const",
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -126,20 +124,13 @@ Simulation {
|
||||||
index: StatePartIndex<BigSlots>(0),
|
index: StatePartIndex<BigSlots>(0),
|
||||||
ty: UInt<8>,
|
ty: UInt<8>,
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x05,
|
state: 0x05,
|
||||||
last_state: 0x05,
|
last_state: 0x05,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
trace_memories: {},
|
trace_memories: {},
|
||||||
trace_writers: [],
|
trace_writers: [],
|
||||||
clocks_triggered: [],
|
|
||||||
event_queue: EventQueue(EventQueueData {
|
|
||||||
instant: 0 s,
|
instant: 0 s,
|
||||||
events: {},
|
clocks_triggered: [],
|
||||||
}),
|
|
||||||
waiting_sensitivity_sets_by_address: {},
|
|
||||||
waiting_sensitivity_sets_by_compiled_value: {},
|
|
||||||
asserts: [],
|
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ Simulation {
|
||||||
},
|
},
|
||||||
SlotDebugData {
|
SlotDebugData {
|
||||||
name: "",
|
name: "",
|
||||||
ty: UInt<1>,
|
ty: Bool,
|
||||||
},
|
},
|
||||||
SlotDebugData {
|
SlotDebugData {
|
||||||
name: "",
|
name: "",
|
||||||
|
|
@ -51,12 +51,12 @@ Simulation {
|
||||||
insns: [
|
insns: [
|
||||||
// at: module-XXXXXXXXXX.rs:1:1
|
// at: module-XXXXXXXXXX.rs:1:1
|
||||||
0: Const {
|
0: Const {
|
||||||
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
|
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
|
||||||
value: 0x1,
|
value: 0x1,
|
||||||
},
|
},
|
||||||
1: Copy {
|
1: Copy {
|
||||||
dest: StatePartIndex<BigSlots>(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset },
|
dest: StatePartIndex<BigSlots>(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset },
|
||||||
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
|
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
|
||||||
},
|
},
|
||||||
// at: module-XXXXXXXXXX.rs:4:1
|
// at: module-XXXXXXXXXX.rs:4:1
|
||||||
2: Copy {
|
2: Copy {
|
||||||
|
|
@ -80,7 +80,6 @@ Simulation {
|
||||||
},
|
},
|
||||||
pc: 5,
|
pc: 5,
|
||||||
memory_write_log: [],
|
memory_write_log: [],
|
||||||
assert_failed_log: [],
|
|
||||||
memories: StatePart {
|
memories: StatePart {
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
|
|
@ -107,7 +106,6 @@ Simulation {
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
global_io: {},
|
|
||||||
main_module: SimulationModuleState {
|
main_module: SimulationModuleState {
|
||||||
base_targets: [
|
base_targets: [
|
||||||
Instance {
|
Instance {
|
||||||
|
|
@ -143,9 +141,9 @@ Simulation {
|
||||||
}.reset_out,
|
}.reset_out,
|
||||||
},
|
},
|
||||||
did_initial_settle: true,
|
did_initial_settle: true,
|
||||||
clocks_for_past: {},
|
|
||||||
},
|
},
|
||||||
extern_modules: [],
|
extern_modules: [],
|
||||||
|
state_ready_to_run: false,
|
||||||
trace_decls: TraceModule {
|
trace_decls: TraceModule {
|
||||||
name: "connect_const_reset",
|
name: "connect_const_reset",
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -177,7 +175,6 @@ Simulation {
|
||||||
kind: BigAsyncReset {
|
kind: BigAsyncReset {
|
||||||
index: StatePartIndex<BigSlots>(0),
|
index: StatePartIndex<BigSlots>(0),
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x1,
|
state: 0x1,
|
||||||
last_state: 0x1,
|
last_state: 0x1,
|
||||||
},
|
},
|
||||||
|
|
@ -186,7 +183,6 @@ Simulation {
|
||||||
kind: BigBool {
|
kind: BigBool {
|
||||||
index: StatePartIndex<BigSlots>(1),
|
index: StatePartIndex<BigSlots>(1),
|
||||||
},
|
},
|
||||||
maybe_changed: true,
|
|
||||||
state: 0x1,
|
state: 0x1,
|
||||||
last_state: 0x1,
|
last_state: 0x1,
|
||||||
},
|
},
|
||||||
|
|
@ -201,13 +197,7 @@ Simulation {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
clocks_triggered: [],
|
|
||||||
event_queue: EventQueue(EventQueueData {
|
|
||||||
instant: 1 μs,
|
instant: 1 μs,
|
||||||
events: {},
|
clocks_triggered: [],
|
||||||
}),
|
|
||||||
waiting_sensitivity_sets_by_address: {},
|
|
||||||
waiting_sensitivity_sets_by_compiled_value: {},
|
|
||||||
asserts: [],
|
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
$timescale 1 ps $end
|
$timescale 1 ps $end
|
||||||
$scope module connect_const_reset $end
|
$scope module connect_const_reset $end
|
||||||
$var wire 1 8ke|= reset_out $end
|
$var wire 1 ! reset_out $end
|
||||||
$var wire 1 {"c@= bit_out $end
|
$var wire 1 " bit_out $end
|
||||||
$upscope $end
|
$upscope $end
|
||||||
$enddefinitions $end
|
$enddefinitions $end
|
||||||
$dumpvars
|
$dumpvars
|
||||||
18ke|=
|
1!
|
||||||
1{"c@=
|
1"
|
||||||
$end
|
$end
|
||||||
#1000000
|
#1000000
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue