forked from libre-chip/fayalite
Compare commits
54 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbe4585578 | |||
| e4210a672f | |||
| e54558d848 | |||
| 46f3519c76 | |||
| 9e803223d0 | |||
| 2a65aa2bd5 | |||
| 2817cd3d58 | |||
| 053c1b2b10 | |||
| 17b58e8edb | |||
| df020e9c9b | |||
| 45fea70c18 | |||
| fbc8ffa5ae | |||
| 26a7090178 | |||
| 0b77d1bea0 | |||
| 840c5e1895 | |||
| c11a1743f9 | |||
| 0be9f9ce23 | |||
| 0b82178740 | |||
| 4b24a88641 | |||
| 094c77e26e | |||
| d2c8b023bf | |||
| c043ee54d0 | |||
| edcc5927a5 | |||
| 7dc4417874 | |||
| 838bd469ce | |||
| b6e4cd0614 | |||
| 3e5b2f126a | |||
| 040cefea21 | |||
| 3267cb38c4 | |||
| b3cc28e2b6 | |||
| 26840daf13 | |||
| 4d9e8d3b47 | |||
| c6feea6d51 | |||
| 409992961c | |||
| 2bdc8a7c72 | |||
| 477a1f2d29 | |||
| 4d54f903be | |||
| 3f5dd61e46 | |||
| def406ab52 | |||
| a565be1b09 | |||
| 676c1e3b7d | |||
| 169be960f8 | |||
| 2b52799f5c | |||
| 35f98f3229 | |||
| 8a63ea89d0 | |||
| 84c5978eaf | |||
| 42e3179a60 | |||
| 53ae3ff670 | |||
| 7af9abfb6f | |||
| aacd05378f | |||
| 908ccef674 | |||
| 057670c12a | |||
| f8ac78abd6 | |||
| 64ec6c0dcc |
129 changed files with 53020 additions and 4371 deletions
|
|
@ -1,77 +0,0 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
# See Notices.txt for copyright information
|
||||
on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
cache-primary-key:
|
||||
value: ${{ jobs.deps.outputs.cache-primary-key }}
|
||||
|
||||
jobs:
|
||||
deps:
|
||||
runs-on: debian-12
|
||||
outputs:
|
||||
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
||||
steps:
|
||||
- uses: https://git.libre-chip.org/mirrors/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
|
||||
id: restore-deps
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ github.repository }}-deps-${{ runner.os }}-${{ hashFiles('.forgejo/workflows/deps.yml') }}
|
||||
lookup-only: true
|
||||
- name: Install Apt packages
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
apt-get update -qq
|
||||
apt-get install -qq \
|
||||
bison \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang \
|
||||
cvc5 \
|
||||
flex \
|
||||
gawk \
|
||||
g++ \
|
||||
git \
|
||||
libboost-filesystem-dev \
|
||||
libboost-python-dev \
|
||||
libboost-system-dev \
|
||||
libffi-dev \
|
||||
libreadline-dev \
|
||||
lld \
|
||||
pkg-config \
|
||||
python3 \
|
||||
python3-click \
|
||||
tcl-dev \
|
||||
zlib1g-dev
|
||||
- name: Install Firtool
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p deps
|
||||
wget -O deps/firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz
|
||||
sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 deps/firrtl.tar.gz'
|
||||
tar -C deps -xvaf deps/firrtl.tar.gz
|
||||
rm -rf deps/firtool
|
||||
mv deps/firtool-1.86.0 deps/firtool
|
||||
- name: Get SymbiYosys
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby
|
||||
- name: Build Z3
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3
|
||||
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
|
||||
make -C deps/z3/build -j"$(nproc)"
|
||||
- name: Build Yosys
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys
|
||||
make -C deps/yosys -j"$(nproc)"
|
||||
- uses: https://git.libre-chip.org/mirrors/cache/save@v3
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
||||
|
|
@ -3,57 +3,16 @@
|
|||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
deps:
|
||||
runs-on: debian-12
|
||||
uses: ./.forgejo/workflows/deps.yml
|
||||
test:
|
||||
runs-on: debian-12
|
||||
needs: deps
|
||||
container:
|
||||
image: git.libre-chip.org/libre-chip/fayalite-deps:latest
|
||||
steps:
|
||||
- uses: https://git.libre-chip.org/mirrors/checkout@v3
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: |
|
||||
scripts/check-copyright.sh
|
||||
- run: |
|
||||
apt-get update -qq
|
||||
apt-get install -qq \
|
||||
bison \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang \
|
||||
cvc5 \
|
||||
flex \
|
||||
gawk \
|
||||
git \
|
||||
libboost-filesystem-dev \
|
||||
libboost-python-dev \
|
||||
libboost-system-dev \
|
||||
libffi-dev \
|
||||
libreadline-dev \
|
||||
lld \
|
||||
pkg-config \
|
||||
python3 \
|
||||
python3-click \
|
||||
tcl-dev \
|
||||
z3 \
|
||||
zlib1g-dev
|
||||
- run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.89.0
|
||||
source "$HOME/.cargo/env"
|
||||
rustup component add rust-src
|
||||
echo "$PATH" >> "$GITHUB_PATH"
|
||||
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ needs.deps.outputs.cache-primary-key }}
|
||||
fail-on-cache-miss: true
|
||||
- run: |
|
||||
make -C deps/z3/build install
|
||||
make -C deps/sby install
|
||||
make -C deps/yosys install
|
||||
export PATH="$(realpath deps/firtool/bin):$PATH"
|
||||
echo "$PATH" >> "$GITHUB_PATH"
|
||||
- uses: https://git.libre-chip.org/mirrors/rust-cache@v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
|
@ -62,3 +21,5 @@ jobs:
|
|||
- run: cargo test --doc --features=unstable-doc
|
||||
- run: cargo doc --features=unstable-doc
|
||||
- run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher
|
||||
- run: cargo run --example blinky yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/blinky-out
|
||||
- run: cargo run --example tx_only_uart yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/tx_only_uart-out
|
||||
|
|
|
|||
128
Cargo.lock
generated
128
Cargo.lock
generated
|
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
|
|
@ -25,9 +25,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.7"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
|
|
@ -81,6 +81,12 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.8"
|
||||
|
|
@ -149,9 +155,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.9"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
||||
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -159,9 +165,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.9"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
||||
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -170,10 +176,19 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.8"
|
||||
name = "clap_complete"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||
checksum = "75bf0b32ad2e152de789bb635ea4d3078f6b838ad7974143e99b99f45a04af4a"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
|
@ -183,9 +198,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.1"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
|
|
@ -291,9 +306,11 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
|||
name = "fayalite"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitvec",
|
||||
"blake3",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"ctor",
|
||||
"eyre",
|
||||
"fayalite-proc-macros",
|
||||
|
|
@ -302,7 +319,7 @@ dependencies = [
|
|||
"jobslot",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"os_pipe",
|
||||
"ordered-float",
|
||||
"petgraph",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -377,12 +394,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.14"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
|
|
@ -449,23 +467,23 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
|||
|
||||
[[package]]
|
||||
name = "jobslot"
|
||||
version = "0.2.19"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d"
|
||||
checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"derive_destructure2",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"scopeguard",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
version = "0.2.176"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
|
@ -508,13 +526,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.1"
|
||||
name = "ordered-float"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
||||
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -557,12 +576,37 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.31"
|
||||
|
|
@ -748,9 +792,21 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
version = "0.14.7+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
|
||||
dependencies = [
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
|
|
@ -795,6 +851,12 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
|
@ -806,11 +868,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -883,6 +945,12 @@ version = "0.0.19"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
|
|
|
|||
|
|
@ -18,17 +18,19 @@ fayalite-proc-macros = { version = "=0.3.0", path = "crates/fayalite-proc-macros
|
|||
fayalite-proc-macros-impl = { version = "=0.3.0", path = "crates/fayalite-proc-macros-impl" }
|
||||
fayalite-visit-gen = { version = "=0.3.0", path = "crates/fayalite-visit-gen" }
|
||||
base16ct = "0.2.0"
|
||||
base64 = "0.22.1"
|
||||
bitvec = { version = "1.0.1", features = ["serde"] }
|
||||
blake3 = { version = "1.5.4", features = ["serde"] }
|
||||
clap = { version = "4.5.9", features = ["derive", "env", "string"] }
|
||||
clap_complete = "4.5.58"
|
||||
ctor = "0.2.8"
|
||||
eyre = "0.6.12"
|
||||
hashbrown = "0.15.2"
|
||||
indexmap = { version = "2.5.0", features = ["serde"] }
|
||||
jobslot = "0.2.19"
|
||||
jobslot = "0.2.23"
|
||||
num-bigint = "0.4.6"
|
||||
num-traits = "0.2.16"
|
||||
os_pipe = "1.2.1"
|
||||
ordered-float = { version = "5.1.0", features = ["serde"] }
|
||||
petgraph = "0.8.1"
|
||||
prettyplease = "0.2.20"
|
||||
proc-macro2 = "1.0.83"
|
||||
|
|
|
|||
67
README.md
67
README.md
|
|
@ -8,6 +8,73 @@ Fayalite is a library for designing digital hardware -- a hardware description l
|
|||
|
||||
[FIRRTL]: https://github.com/chipsalliance/firrtl-spec
|
||||
|
||||
# Building the [Blinky example] for the Arty A7 100T on Linux
|
||||
|
||||
[Blinky example]: crates/fayalite/examples/blinky.rs
|
||||
|
||||
This uses the container image containing all the external programs and files that Fayalite needs to build for FPGAs, the sources for the container image are in <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
|
||||
|
||||
## NLnet Grants
|
||||
|
|
|
|||
|
|
@ -87,7 +87,11 @@ impl ParsedBundle {
|
|||
no_static: _,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq: _,
|
||||
ref get,
|
||||
} = options.body;
|
||||
if let Some((get, ..)) = get {
|
||||
errors.error(get, "#[hdl(get(...))] is not allowed on structs");
|
||||
}
|
||||
let mut fields = match fields {
|
||||
syn::Fields::Named(fields) => fields,
|
||||
syn::Fields::Unnamed(fields) => {
|
||||
|
|
@ -220,7 +224,7 @@ impl Builder {
|
|||
.args
|
||||
.push_value(match get_field_state(field_index) {
|
||||
BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=>
|
||||
::fayalite::bundle::Unfilled<#ty>
|
||||
()
|
||||
},
|
||||
BuilderFieldState::Generic => {
|
||||
let type_var = type_var_for_field_name(ident);
|
||||
|
|
@ -345,7 +349,6 @@ impl ToTokens for Builder {
|
|||
}
|
||||
}));
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, non_snake_case, dead_code)]
|
||||
impl #impl_generics #unfilled_ty
|
||||
#where_clause
|
||||
|
|
@ -380,7 +383,7 @@ impl ToTokens for Builder {
|
|||
fn default() -> Self {
|
||||
#ident {
|
||||
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||
#(#field_idents: ::fayalite::__std::default::Default::default(),)*
|
||||
#(#field_idents: (),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -392,16 +395,30 @@ impl ToTokens for Builder {
|
|||
let type_generics = self.generics.split_for_impl().1;
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
|
||||
#[allow(non_camel_case_types, dead_code, private_interfaces)]
|
||||
impl #filled_impl_generics ::fayalite::expr::ValueType for #filled_ty
|
||||
#filled_where_clause
|
||||
{
|
||||
type Type = #target #type_generics;
|
||||
type ValueCategory = ::fayalite::expr::value_category::ValueCategoryExpr;
|
||||
|
||||
fn ty(&self) -> <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
|
||||
#filled_where_clause
|
||||
{
|
||||
fn to_expr(
|
||||
&self,
|
||||
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> {
|
||||
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ValueType>::Type> {
|
||||
let __ty = #target {
|
||||
#(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)*
|
||||
#(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)*
|
||||
};
|
||||
let __field_values = [
|
||||
#(::fayalite::expr::Expr::canonical(self.#field_idents),)*
|
||||
|
|
@ -446,6 +463,7 @@ impl ToTokens for ParsedBundle {
|
|||
no_static,
|
||||
no_runtime_generics,
|
||||
cmp_eq,
|
||||
get: _,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut item_attrs = attrs.clone();
|
||||
|
|
@ -495,7 +513,6 @@ impl ToTokens for ParsedBundle {
|
|||
};
|
||||
builder.to_tokens(tokens);
|
||||
let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||
let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||
let mut mask_type_fields = FieldsNamed::from(fields.clone());
|
||||
for Field { ty, .. } in &mut mask_type_fields.named {
|
||||
*ty = parse_quote_spanned! {span=>
|
||||
|
|
@ -513,8 +530,6 @@ impl ToTokens for ParsedBundle {
|
|||
mask_type_builder.to_tokens(tokens);
|
||||
let unfilled_mask_type_builder_ty =
|
||||
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||
let filled_mask_type_builder_ty =
|
||||
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||
ItemStruct {
|
||||
attrs: vec![
|
||||
common_derives(span),
|
||||
|
|
@ -694,10 +709,10 @@ impl ToTokens for ParsedBundle {
|
|||
v.field(&value.#ident);
|
||||
}
|
||||
}));
|
||||
let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let value_type_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
quote_spanned! {span=>
|
||||
#ident: ::fayalite::sim::value::SimValue::ty(&self.#ident),
|
||||
#ident: ::fayalite::expr::ValueType::ty(&self.#ident),
|
||||
}
|
||||
}));
|
||||
let fields_len = fields.named().into_iter().len();
|
||||
|
|
@ -781,7 +796,6 @@ impl ToTokens for ParsedBundle {
|
|||
#where_clause
|
||||
{
|
||||
type Builder = #unfilled_mask_type_builder_ty;
|
||||
type FilledBuilder = #filled_mask_type_builder_ty;
|
||||
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||
}
|
||||
|
|
@ -806,28 +820,39 @@ impl ToTokens for ParsedBundle {
|
|||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics
|
||||
impl #impl_generics ::fayalite::expr::ValueType for #mask_type_sim_value_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Type = #mask_type_ident #type_generics;
|
||||
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
|
||||
|
||||
fn ty(&self) -> <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
|
||||
#where_clause
|
||||
{
|
||||
fn to_sim_value(
|
||||
&self,
|
||||
) -> ::fayalite::sim::value::SimValue<
|
||||
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||
<Self as ::fayalite::expr::ValueType>::Type,
|
||||
> {
|
||||
let ty = #mask_type_ident {
|
||||
#(#to_sim_value_fields)*
|
||||
#(#value_type_fields)*
|
||||
};
|
||||
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
|
||||
}
|
||||
fn into_sim_value(
|
||||
self,
|
||||
) -> ::fayalite::sim::value::SimValue<
|
||||
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||
<Self as ::fayalite::expr::ValueType>::Type,
|
||||
> {
|
||||
let ty = #mask_type_ident {
|
||||
#(#to_sim_value_fields)*
|
||||
#(#value_type_fields)*
|
||||
};
|
||||
::fayalite::sim::value::SimValue::from_value(ty, self)
|
||||
}
|
||||
|
|
@ -931,7 +956,6 @@ impl ToTokens for ParsedBundle {
|
|||
#where_clause
|
||||
{
|
||||
type Builder = #unfilled_builder_ty;
|
||||
type FilledBuilder = #filled_builder_ty;
|
||||
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||
}
|
||||
|
|
@ -956,28 +980,39 @@ impl ToTokens for ParsedBundle {
|
|||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
|
||||
impl #impl_generics ::fayalite::expr::ValueType for #sim_value_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Type = #target #type_generics;
|
||||
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
|
||||
|
||||
fn ty(&self) -> <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
|
||||
#where_clause
|
||||
{
|
||||
fn to_sim_value(
|
||||
&self,
|
||||
) -> ::fayalite::sim::value::SimValue<
|
||||
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||
<Self as ::fayalite::expr::ValueType>::Type,
|
||||
> {
|
||||
let ty = #target {
|
||||
#(#to_sim_value_fields)*
|
||||
#(#value_type_fields)*
|
||||
};
|
||||
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
|
||||
}
|
||||
fn into_sim_value(
|
||||
self,
|
||||
) -> ::fayalite::sim::value::SimValue<
|
||||
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||
<Self as ::fayalite::expr::ValueType>::Type,
|
||||
> {
|
||||
let ty = #target {
|
||||
#(#to_sim_value_fields)*
|
||||
#(#value_type_fields)*
|
||||
};
|
||||
::fayalite::sim::value::SimValue::from_value(ty, self)
|
||||
}
|
||||
|
|
@ -1003,91 +1038,172 @@ impl ToTokens for ParsedBundle {
|
|||
}
|
||||
.to_tokens(tokens);
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
let mut expr_where_clause =
|
||||
let mut cmp_eq_where_clause =
|
||||
Generics::from(generics)
|
||||
.where_clause
|
||||
.unwrap_or_else(|| syn::WhereClause {
|
||||
where_token: Token,
|
||||
predicates: Punctuated::new(),
|
||||
});
|
||||
let mut sim_value_where_clause = expr_where_clause.clone();
|
||||
let mut fields_sim_value_eq = vec![];
|
||||
let mut fields_cmp_eq = vec![];
|
||||
let mut fields_cmp_ne = vec![];
|
||||
let mut fields_value_eq = vec![];
|
||||
let mut fields_value_ne = vec![];
|
||||
let mut fields_expr_eq = vec![];
|
||||
let mut fields_expr_ne = vec![];
|
||||
let mut fields_valueless_eq = vec![];
|
||||
let mut fields_valueless_ne = vec![];
|
||||
for field in fields.named() {
|
||||
let field_ident = field.ident();
|
||||
let field_ty = field.ty();
|
||||
expr_where_clause
|
||||
cmp_eq_where_clause
|
||||
.predicates
|
||||
.push(parse_quote_spanned! {cmp_eq.span=>
|
||||
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
|
||||
#field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty>
|
||||
});
|
||||
sim_value_where_clause
|
||||
.predicates
|
||||
.push(parse_quote_spanned! {cmp_eq.span=>
|
||||
#field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty>
|
||||
fields_value_eq.push(quote_spanned! {span=>
|
||||
::fayalite::expr::HdlPartialEqImpl::cmp_value_eq(
|
||||
__lhs.#field_ident,
|
||||
::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident),
|
||||
__rhs.#field_ident,
|
||||
::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident),
|
||||
)
|
||||
});
|
||||
fields_sim_value_eq.push(quote_spanned! {span=>
|
||||
::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident)
|
||||
fields_value_ne.push(quote_spanned! {span=>
|
||||
::fayalite::expr::HdlPartialEqImpl::cmp_value_ne(
|
||||
__lhs.#field_ident,
|
||||
::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident),
|
||||
__rhs.#field_ident,
|
||||
::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident),
|
||||
)
|
||||
});
|
||||
fields_cmp_eq.push(quote_spanned! {span=>
|
||||
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
|
||||
fields_expr_eq.push(quote_spanned! {span=>
|
||||
::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(
|
||||
__lhs.#field_ident,
|
||||
__rhs.#field_ident,
|
||||
)
|
||||
});
|
||||
fields_cmp_ne.push(quote_spanned! {span=>
|
||||
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
|
||||
fields_expr_ne.push(quote_spanned! {span=>
|
||||
::fayalite::expr::HdlPartialEqImpl::cmp_expr_ne(
|
||||
__lhs.#field_ident,
|
||||
__rhs.#field_ident,
|
||||
)
|
||||
});
|
||||
fields_valueless_eq.push(quote_spanned! {span=>
|
||||
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq(
|
||||
::fayalite::expr::Valueless::new(__lhs.#field_ident),
|
||||
::fayalite::expr::Valueless::new(__rhs.#field_ident),
|
||||
)
|
||||
});
|
||||
fields_valueless_ne.push(quote_spanned! {span=>
|
||||
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_ne(
|
||||
::fayalite::expr::Valueless::new(__lhs.#field_ident),
|
||||
::fayalite::expr::Valueless::new(__rhs.#field_ident),
|
||||
)
|
||||
});
|
||||
}
|
||||
let sim_value_eq_body;
|
||||
let cmp_eq_body;
|
||||
let cmp_ne_body;
|
||||
let value_eq_body;
|
||||
let value_ne_body;
|
||||
let expr_eq_body;
|
||||
let expr_ne_body;
|
||||
let valueless_eq_body;
|
||||
let valueless_ne_body;
|
||||
if fields_len == 0 {
|
||||
sim_value_eq_body = quote_spanned! {span=>
|
||||
value_eq_body = quote_spanned! {span=>
|
||||
true
|
||||
};
|
||||
cmp_eq_body = quote_spanned! {span=>
|
||||
value_ne_body = quote_spanned! {span=>
|
||||
false
|
||||
};
|
||||
expr_eq_body = quote_spanned! {span=>
|
||||
::fayalite::expr::ToExpr::to_expr(&true)
|
||||
};
|
||||
cmp_ne_body = quote_spanned! {span=>
|
||||
expr_ne_body = quote_spanned! {span=>
|
||||
::fayalite::expr::ToExpr::to_expr(&false)
|
||||
};
|
||||
valueless_eq_body = quote_spanned! {span=>
|
||||
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
|
||||
};
|
||||
valueless_ne_body = quote_spanned! {span=>
|
||||
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
|
||||
};
|
||||
} else {
|
||||
sim_value_eq_body = quote_spanned! {span=>
|
||||
#(#fields_sim_value_eq)&&*
|
||||
value_eq_body = quote_spanned! {span=>
|
||||
#(#fields_value_eq)&*
|
||||
};
|
||||
cmp_eq_body = quote_spanned! {span=>
|
||||
#(#fields_cmp_eq)&*
|
||||
value_ne_body = quote_spanned! {span=>
|
||||
#(#fields_value_ne)|*
|
||||
};
|
||||
cmp_ne_body = quote_spanned! {span=>
|
||||
#(#fields_cmp_ne)|*
|
||||
expr_eq_body = quote_spanned! {span=>
|
||||
#(#fields_expr_eq)&*
|
||||
};
|
||||
expr_ne_body = quote_spanned! {span=>
|
||||
#(#fields_expr_ne)|*
|
||||
};
|
||||
valueless_eq_body = quote_spanned! {span=>
|
||||
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
|
||||
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
|
||||
#(#fields_valueless_eq)|*
|
||||
};
|
||||
valueless_ne_body = quote_spanned! {span=>
|
||||
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
|
||||
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
|
||||
#(#fields_valueless_ne)|*
|
||||
};
|
||||
};
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
|
||||
#expr_where_clause
|
||||
impl #impl_generics ::fayalite::expr::HdlPartialEqImpl<Self> for #target #type_generics
|
||||
#cmp_eq_where_clause
|
||||
{
|
||||
fn cmp_eq(
|
||||
#[track_caller]
|
||||
fn cmp_value_eq(
|
||||
__lhs: Self,
|
||||
__lhs_value: ::fayalite::__std::borrow::Cow<'_, <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>,
|
||||
__rhs: ::fayalite::expr::Expr<Self>,
|
||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||
#cmp_eq_body
|
||||
#expr_eq_body
|
||||
}
|
||||
fn cmp_ne(
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_expr_ne(
|
||||
__lhs: ::fayalite::expr::Expr<Self>,
|
||||
__rhs: ::fayalite::expr::Expr<Self>,
|
||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||
#cmp_ne_body
|
||||
#expr_ne_body
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::sim::value::SimValuePartialEq<Self> for #target #type_generics
|
||||
#sim_value_where_clause
|
||||
{
|
||||
fn sim_value_eq(
|
||||
__lhs: &::fayalite::sim::value::SimValue<Self>,
|
||||
__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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,10 +159,14 @@ impl ParsedEnum {
|
|||
no_static: _,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq,
|
||||
ref get,
|
||||
} = options.body;
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
|
||||
}
|
||||
if let Some((get, ..)) = get {
|
||||
errors.error(get, "#[hdl(get(...))] is not allowed on enums");
|
||||
}
|
||||
attrs.retain(|attr| {
|
||||
if attr.path().is_ident("repr") {
|
||||
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
|
||||
|
|
@ -225,6 +229,7 @@ impl ToTokens for ParsedEnum {
|
|||
no_static,
|
||||
no_runtime_generics,
|
||||
cmp_eq: _, // TODO: implement cmp_eq for enums
|
||||
get: _,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut struct_attrs = attrs.clone();
|
||||
|
|
@ -549,7 +554,6 @@ impl ToTokens for ParsedEnum {
|
|||
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
|
||||
if let Some(ParsedVariantField { ty, .. }) = field {
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
|
|
@ -571,7 +575,6 @@ impl ToTokens for ParsedEnum {
|
|||
)
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #sim_builder_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
|
|
@ -593,7 +596,6 @@ impl ToTokens for ParsedEnum {
|
|||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
|
|
@ -608,7 +610,6 @@ impl ToTokens for ParsedEnum {
|
|||
)
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #sim_builder_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
|
|
@ -1023,16 +1024,26 @@ impl ToTokens for ParsedEnum {
|
|||
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #static_impl_generics ::fayalite::sim::value::ToSimValue
|
||||
impl #static_impl_generics ::fayalite::expr::ValueType
|
||||
for #sim_value_ident #static_type_generics
|
||||
#static_where_clause
|
||||
{
|
||||
type Type = #target #static_type_generics;
|
||||
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
|
||||
|
||||
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
|
||||
::fayalite::ty::StaticType::TYPE
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #static_impl_generics ::fayalite::sim::value::ToSimValue
|
||||
for #sim_value_ident #static_type_generics
|
||||
#static_where_clause
|
||||
{
|
||||
fn to_sim_value(
|
||||
&self,
|
||||
) -> ::fayalite::sim::value::SimValue<
|
||||
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||
<Self as ::fayalite::expr::ValueType>::Type,
|
||||
> {
|
||||
::fayalite::sim::value::SimValue::from_value(
|
||||
::fayalite::ty::StaticType::TYPE,
|
||||
|
|
@ -1042,7 +1053,7 @@ impl ToTokens for ParsedEnum {
|
|||
fn into_sim_value(
|
||||
self,
|
||||
) -> ::fayalite::sim::value::SimValue<
|
||||
<Self as ::fayalite::sim::value::ToSimValue>::Type,
|
||||
<Self as ::fayalite::expr::ValueType>::Type,
|
||||
> {
|
||||
::fayalite::sim::value::SimValue::from_value(
|
||||
::fayalite::ty::StaticType::TYPE,
|
||||
|
|
|
|||
|
|
@ -3,29 +3,264 @@
|
|||
use crate::{
|
||||
Errors, HdlAttr,
|
||||
hdl_type_common::{
|
||||
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, TypesParser,
|
||||
get_target,
|
||||
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType,
|
||||
PhantomConstGetBound, TypesParser, WrappedInConst, common_derives, get_target, known_items,
|
||||
},
|
||||
kw,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{Attribute, Generics, Ident, ItemType, Token, Type, Visibility, parse_quote_spanned};
|
||||
use quote::{ToTokens, format_ident, quote_spanned};
|
||||
use syn::{
|
||||
Attribute, Expr, Fields, GenericParam, Generics, Ident, ItemStruct, ItemType, Token, Type,
|
||||
TypeGroup, TypeParam, TypeParen, Visibility, parse_quote_spanned, punctuated::Pair,
|
||||
token::Paren,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedTypeAlias {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) type_token: Token![type],
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
|
||||
pub(crate) eq_token: Token![=],
|
||||
pub(crate) ty: MaybeParsed<ParsedType, Type>,
|
||||
pub(crate) semi_token: Token![;],
|
||||
pub(crate) struct PhantomConstAccessorTypeParam {
|
||||
attrs: Vec<Attribute>,
|
||||
ident: Ident,
|
||||
colon_token: Token![:],
|
||||
phantom_const_get_bound: PhantomConstGetBound,
|
||||
plus_token: Option<Token![+]>,
|
||||
}
|
||||
|
||||
impl From<PhantomConstAccessorTypeParam> for TypeParam {
|
||||
fn from(value: PhantomConstAccessorTypeParam) -> Self {
|
||||
let PhantomConstAccessorTypeParam {
|
||||
attrs,
|
||||
ident,
|
||||
colon_token,
|
||||
phantom_const_get_bound,
|
||||
plus_token,
|
||||
} = value;
|
||||
TypeParam {
|
||||
attrs,
|
||||
ident,
|
||||
colon_token: Some(colon_token),
|
||||
bounds: FromIterator::from_iter([Pair::new(
|
||||
phantom_const_get_bound.into(),
|
||||
plus_token,
|
||||
)]),
|
||||
eq_token: None,
|
||||
default: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<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 {
|
||||
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: _,
|
||||
} = options.body;
|
||||
if let Some((no_static,)) = no_static {
|
||||
errors.error(no_static, "no_static is not valid on type aliases");
|
||||
}
|
||||
if let Some((target, ..)) = target {
|
||||
errors.error(
|
||||
target,
|
||||
"target is not implemented on PhantomConstGet type aliases",
|
||||
);
|
||||
}
|
||||
if let Some((no_runtime_generics,)) = no_runtime_generics {
|
||||
errors.error(
|
||||
no_runtime_generics,
|
||||
"no_runtime_generics is not implemented on PhantomConstGet type aliases",
|
||||
);
|
||||
}
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
|
||||
}
|
||||
if let Some((custom_bounds,)) = custom_bounds {
|
||||
errors.error(
|
||||
custom_bounds,
|
||||
"custom_bounds is not implemented on PhantomConstGet type aliases",
|
||||
);
|
||||
}
|
||||
let Some(generics) = PhantomConstAccessorGenerics::parse_opt(generics) else {
|
||||
errors.error(ident, "#[hdl(get(...))] type alias must be of the form:\ntype MyTypeGetter<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> {
|
||||
let ItemType {
|
||||
mut attrs,
|
||||
|
|
@ -51,7 +286,25 @@ impl ParsedTypeAlias {
|
|||
no_static,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq,
|
||||
ref mut get,
|
||||
} = options.body;
|
||||
if let Some(get) = get.take() {
|
||||
return Self::parse_phantom_const_accessor(
|
||||
ItemType {
|
||||
attrs,
|
||||
vis,
|
||||
type_token,
|
||||
ident,
|
||||
generics,
|
||||
eq_token,
|
||||
ty,
|
||||
semi_token,
|
||||
},
|
||||
errors,
|
||||
options,
|
||||
get,
|
||||
);
|
||||
}
|
||||
if let Some((no_static,)) = no_static {
|
||||
errors.error(no_static, "no_static is not valid on type aliases");
|
||||
}
|
||||
|
|
@ -67,7 +320,7 @@ impl ParsedTypeAlias {
|
|||
};
|
||||
let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors);
|
||||
errors.finish()?;
|
||||
Ok(Self {
|
||||
Ok(Self::TypeAlias {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
|
|
@ -83,7 +336,8 @@ impl ParsedTypeAlias {
|
|||
|
||||
impl ToTokens for ParsedTypeAlias {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
match self {
|
||||
Self::TypeAlias {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
|
|
@ -93,7 +347,7 @@ impl ToTokens for ParsedTypeAlias {
|
|||
eq_token,
|
||||
ty,
|
||||
semi_token,
|
||||
} = self;
|
||||
} => {
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
target,
|
||||
|
|
@ -101,6 +355,7 @@ impl ToTokens for ParsedTypeAlias {
|
|||
no_static: _,
|
||||
no_runtime_generics,
|
||||
cmp_eq: _,
|
||||
get: _,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut type_attrs = attrs.clone();
|
||||
|
|
@ -126,11 +381,110 @@ 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: _,
|
||||
} = &options.body;
|
||||
let span = ident.span();
|
||||
let mut type_attrs = attrs.clone();
|
||||
type_attrs.push(parse_quote_spanned! {span=>
|
||||
#[allow(type_alias_bounds)]
|
||||
});
|
||||
let type_param_ident = &generics.type_param.ident;
|
||||
let syn_generics = Generics::from(generics);
|
||||
ItemType {
|
||||
attrs: type_attrs,
|
||||
vis: vis.clone(),
|
||||
type_token: *type_token,
|
||||
ident: ident.clone(),
|
||||
generics: syn_generics.clone(),
|
||||
eq_token: *eq_token,
|
||||
ty: parse_quote_spanned! {span=>
|
||||
<#ty as ::fayalite::phantom_const::ReturnSelfUnchanged<#type_param_ident>>::Type
|
||||
},
|
||||
semi_token: *semi_token,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let generics_accumulation_ident =
|
||||
format_ident!("__{}__GenericsAccumulation", ident);
|
||||
ItemStruct {
|
||||
attrs: vec![
|
||||
common_derives(span),
|
||||
parse_quote_spanned! {span=>
|
||||
#[allow(non_camel_case_types)]
|
||||
},
|
||||
],
|
||||
vis: vis.clone(),
|
||||
struct_token: Token,
|
||||
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> {
|
||||
let item = ParsedTypeAlias::parse(item)?;
|
||||
let outline_generated = item.options.body.outline_generated;
|
||||
let outline_generated = match &item {
|
||||
ParsedTypeAlias::TypeAlias { options, .. }
|
||||
| ParsedTypeAlias::PhantomConstAccessor { options, .. } => options.body.outline_generated,
|
||||
};
|
||||
let mut contents = item.to_token_stream();
|
||||
if outline_generated.is_some() {
|
||||
contents = crate::outline_generated(contents, "hdl-type-alias-");
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ use syn::{
|
|||
AngleBracketedGenericArguments, Attribute, Block, ConstParam, Expr, ExprBlock, ExprGroup,
|
||||
ExprIndex, ExprParen, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed,
|
||||
FieldsUnnamed, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, ItemStruct,
|
||||
Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, Turbofish, Type,
|
||||
TypeGenerics, TypeGroup, TypeParam, TypeParen, TypePath, TypeTuple, Visibility, WhereClause,
|
||||
WherePredicate,
|
||||
Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, TraitBound, Turbofish,
|
||||
Type, TypeGenerics, TypeGroup, TypeParam, TypeParamBound, TypeParen, TypePath, TypeTuple,
|
||||
Visibility, WhereClause, WherePredicate,
|
||||
parse::{Parse, ParseStream},
|
||||
parse_quote, parse_quote_spanned,
|
||||
punctuated::{Pair, Punctuated},
|
||||
|
|
@ -27,6 +27,7 @@ crate::options! {
|
|||
NoStatic(no_static),
|
||||
NoRuntimeGenerics(no_runtime_generics),
|
||||
CmpEq(cmp_eq),
|
||||
Get(get, Expr),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2045,6 +2046,7 @@ pub(crate) mod known_items {
|
|||
impl_known_item!(::fayalite::int::Size);
|
||||
impl_known_item!(::fayalite::int::UInt);
|
||||
impl_known_item!(::fayalite::int::UIntType);
|
||||
impl_known_item!(::fayalite::phantom_const::PhantomConstGet);
|
||||
impl_known_item!(::fayalite::reset::ResetType);
|
||||
impl_known_item!(::fayalite::ty::CanonicalType);
|
||||
impl_known_item!(::fayalite::ty::StaticType);
|
||||
|
|
@ -2063,6 +2065,174 @@ pub(crate) mod known_items {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct PhantomConstGetBound {
|
||||
pub(crate) phantom_const_get: known_items::PhantomConstGet,
|
||||
pub(crate) colon2_token: Option<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 {
|
||||
(
|
||||
#[struct = $struct_type:ident]
|
||||
|
|
@ -2070,6 +2240,10 @@ macro_rules! impl_bounds {
|
|||
$(
|
||||
$Variant:ident,
|
||||
)*
|
||||
$(
|
||||
#[has_body]
|
||||
$VariantHasBody:ident($variant_has_body_ty:ty),
|
||||
)*
|
||||
$(
|
||||
#[unknown]
|
||||
$Unknown:ident,
|
||||
|
|
@ -2079,6 +2253,7 @@ macro_rules! impl_bounds {
|
|||
#[derive(Clone, Debug)]
|
||||
$vis enum $enum_type {
|
||||
$($Variant(known_items::$Variant),)*
|
||||
$($VariantHasBody($variant_has_body_ty),)*
|
||||
$($Unknown(syn::TypeParamBound),)?
|
||||
}
|
||||
|
||||
|
|
@ -2088,31 +2263,42 @@ macro_rules! impl_bounds {
|
|||
}
|
||||
})*
|
||||
|
||||
$(impl From<$variant_has_body_ty> for $enum_type {
|
||||
fn from(v: $variant_has_body_ty) -> Self {
|
||||
Self::$VariantHasBody(v)
|
||||
}
|
||||
})*
|
||||
|
||||
impl ToTokens for $enum_type {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.to_tokens(tokens),)*
|
||||
$(Self::$VariantHasBody(v) => v.to_tokens(tokens),)*
|
||||
$(Self::$Unknown(v) => v.to_tokens(tokens),)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $enum_type {
|
||||
$vis fn parse_path(path: Path) -> Result<Self, Path> {
|
||||
$vis fn parse_path_with_arguments(path: Path) -> syn::Result<Result<Self, Path>> {
|
||||
#![allow(unreachable_code)]
|
||||
$(let path = match known_items::$Variant::parse_path(path) {
|
||||
Ok(v) => return Ok(Self::$Variant(v)),
|
||||
Ok(v) => return Ok(Ok(Self::$Variant(v))),
|
||||
Err(path) => path,
|
||||
};)*
|
||||
$(return Ok(Self::$Unknown(syn::TraitBound {
|
||||
$(let path = match <$variant_has_body_ty>::parse_path_with_arguments(path)? {
|
||||
Ok(v) => return Ok(Ok(Self::$VariantHasBody(v))),
|
||||
Err(path) => path,
|
||||
};)*
|
||||
$(return Ok(Ok(Self::$Unknown(syn::TraitBound {
|
||||
paren_token: None,
|
||||
modifier: syn::TraitBoundModifier::None,
|
||||
lifetimes: None,
|
||||
path,
|
||||
}.into()));)?
|
||||
Err(path)
|
||||
}.into())));)?
|
||||
Ok(Err(path))
|
||||
}
|
||||
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> {
|
||||
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> syn::Result<Result<Self, syn::TypeParamBound>> {
|
||||
#![allow(unreachable_code)]
|
||||
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
|
||||
if let syn::TraitBound {
|
||||
|
|
@ -2121,24 +2307,24 @@ macro_rules! impl_bounds {
|
|||
lifetimes: None,
|
||||
path: _,
|
||||
} = trait_bound {
|
||||
match Self::parse_path(trait_bound.path) {
|
||||
Ok(retval) => return Ok(retval),
|
||||
match Self::parse_path_with_arguments(trait_bound.path)? {
|
||||
Ok(retval) => return Ok(Ok(retval)),
|
||||
Err(path) => trait_bound.path = path,
|
||||
}
|
||||
}
|
||||
type_param_bound = trait_bound.into();
|
||||
}
|
||||
$(return Ok(Self::$Unknown(type_param_bound));)?
|
||||
Err(type_param_bound)
|
||||
$(return Ok(Ok(Self::$Unknown(type_param_bound)));)?
|
||||
Ok(Err(type_param_bound))
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for $enum_type {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Self::parse_type_param_bound(input.parse()?)
|
||||
Self::parse_type_param_bound(input.parse()?)?
|
||||
.map_err(|type_param_bound| syn::Error::new_spanned(
|
||||
type_param_bound,
|
||||
format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
|
||||
format_args!("expected one of: {}", [$(stringify!($Variant),)* $(stringify!($VariantHasBody)),*].join(", ")),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -2147,6 +2333,7 @@ macro_rules! impl_bounds {
|
|||
#[allow(non_snake_case)]
|
||||
$vis struct $struct_type {
|
||||
$($vis $Variant: Option<known_items::$Variant>,)*
|
||||
$($vis $VariantHasBody: Option<$variant_has_body_ty>,)*
|
||||
$($vis $Unknown: Vec<syn::TypeParamBound>,)?
|
||||
}
|
||||
|
||||
|
|
@ -2159,6 +2346,11 @@ macro_rules! impl_bounds {
|
|||
separator = Some(<Token![+]>::default());
|
||||
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 {
|
||||
separator.to_tokens(tokens);
|
||||
separator = Some(<Token![+]>::default());
|
||||
|
|
@ -2172,6 +2364,7 @@ macro_rules! impl_bounds {
|
|||
#[allow(non_snake_case)]
|
||||
$vis struct Iter {
|
||||
$($Variant: Option<known_items::$Variant>,)*
|
||||
$($VariantHasBody: Option<$variant_has_body_ty>,)*
|
||||
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
|
||||
}
|
||||
|
||||
|
|
@ -2182,6 +2375,7 @@ macro_rules! impl_bounds {
|
|||
fn into_iter(self) -> Self::IntoIter {
|
||||
Iter {
|
||||
$($Variant: self.$Variant,)*
|
||||
$($VariantHasBody: self.$VariantHasBody,)*
|
||||
$($Unknown: self.$Unknown.into_iter(),)?
|
||||
}
|
||||
}
|
||||
|
|
@ -2196,6 +2390,11 @@ macro_rules! impl_bounds {
|
|||
return Some($enum_type::$Variant(value));
|
||||
}
|
||||
)*
|
||||
$(
|
||||
if let Some(value) = self.$VariantHasBody.take() {
|
||||
return Some($enum_type::$VariantHasBody(value));
|
||||
}
|
||||
)*
|
||||
$(
|
||||
if let Some(value) = self.$Unknown.next() {
|
||||
return Some($enum_type::$Unknown(value));
|
||||
|
|
@ -2211,6 +2410,11 @@ macro_rules! impl_bounds {
|
|||
init = f(init, $enum_type::$Variant(value));
|
||||
}
|
||||
)*
|
||||
$(
|
||||
if let Some(value) = self.$VariantHasBody.take() {
|
||||
init = f(init, $enum_type::$VariantHasBody(value));
|
||||
}
|
||||
)*
|
||||
$(
|
||||
if let Some(value) = self.$Unknown.next() {
|
||||
init = f(init, $enum_type::$Unknown(value));
|
||||
|
|
@ -2227,6 +2431,9 @@ macro_rules! impl_bounds {
|
|||
$($enum_type::$Variant(v) => {
|
||||
self.$Variant = Some(v);
|
||||
})*
|
||||
$($enum_type::$VariantHasBody(v) => {
|
||||
self.$VariantHasBody = Some(v);
|
||||
})*
|
||||
$($enum_type::$Unknown(v) => {
|
||||
self.$Unknown.push(v);
|
||||
})?
|
||||
|
|
@ -2248,6 +2455,9 @@ macro_rules! impl_bounds {
|
|||
$(if let Some(v) = v.$Variant {
|
||||
self.$Variant = Some(v);
|
||||
})*
|
||||
$(if let Some(v) = v.$VariantHasBody {
|
||||
self.$VariantHasBody = Some(v);
|
||||
})*
|
||||
$(self.$Unknown.extend(v.$Unknown);)*
|
||||
});
|
||||
}
|
||||
|
|
@ -2302,6 +2512,8 @@ impl_bounds! {
|
|||
Size,
|
||||
StaticType,
|
||||
Type,
|
||||
#[has_body]
|
||||
PhantomConstGet(PhantomConstGetBound),
|
||||
#[unknown]
|
||||
Unknown,
|
||||
}
|
||||
|
|
@ -2317,6 +2529,8 @@ impl_bounds! {
|
|||
ResetType,
|
||||
StaticType,
|
||||
Type,
|
||||
#[has_body]
|
||||
PhantomConstGet(PhantomConstGetBound),
|
||||
#[unknown]
|
||||
Unknown,
|
||||
}
|
||||
|
|
@ -2332,6 +2546,7 @@ impl From<ParsedTypeBound> for ParsedBound {
|
|||
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
|
||||
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
|
||||
ParsedTypeBound::Type(v) => ParsedBound::Type(v),
|
||||
ParsedTypeBound::PhantomConstGet(v) => ParsedBound::PhantomConstGet(v),
|
||||
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
|
||||
}
|
||||
}
|
||||
|
|
@ -2347,6 +2562,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
|
|||
ResetType,
|
||||
StaticType,
|
||||
Type,
|
||||
PhantomConstGet,
|
||||
Unknown,
|
||||
} = value;
|
||||
Self {
|
||||
|
|
@ -2359,6 +2575,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
|
|||
Size: None,
|
||||
StaticType,
|
||||
Type,
|
||||
PhantomConstGet,
|
||||
Unknown,
|
||||
}
|
||||
}
|
||||
|
|
@ -2395,6 +2612,10 @@ impl ParsedTypeBound {
|
|||
ParsedTypeBound::Type(known_items::Type(span)),
|
||||
]),
|
||||
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
|
||||
Self::PhantomConstGet(v) => ParsedTypeBounds::from_iter([
|
||||
ParsedTypeBound::from(v),
|
||||
ParsedTypeBound::Type(known_items::Type(span)),
|
||||
]),
|
||||
Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
|
||||
}
|
||||
}
|
||||
|
|
@ -2430,6 +2651,7 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
|
|||
Size,
|
||||
StaticType: None,
|
||||
Type: None,
|
||||
PhantomConstGet: None,
|
||||
Unknown: vec![],
|
||||
}
|
||||
}
|
||||
|
|
@ -2532,6 +2754,9 @@ impl ParsedBound {
|
|||
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
|
||||
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
|
||||
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
|
||||
Self::PhantomConstGet(v) => {
|
||||
ParsedBoundCategory::Type(ParsedTypeBound::PhantomConstGet(v))
|
||||
}
|
||||
Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
|
||||
}
|
||||
}
|
||||
|
|
@ -3417,7 +3642,8 @@ impl ParsedGenerics {
|
|||
| ParsedTypeBound::BundleType(_)
|
||||
| ParsedTypeBound::EnumType(_)
|
||||
| ParsedTypeBound::IntType(_)
|
||||
| ParsedTypeBound::ResetType(_) => {
|
||||
| ParsedTypeBound::ResetType(_)
|
||||
| ParsedTypeBound::PhantomConstGet(_) => {
|
||||
errors.error(bound, "bounds on mask types are not implemented");
|
||||
}
|
||||
ParsedTypeBound::StaticType(bound) => {
|
||||
|
|
@ -4386,3 +4612,124 @@ impl MakeHdlTypeExpr for ParsedTypeTuple {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ParsedSimpleVisibility {
|
||||
Public(Token![pub]),
|
||||
PubCrate {
|
||||
pub_token: Token![pub],
|
||||
paren_token: Paren,
|
||||
crate_token: Token![crate],
|
||||
},
|
||||
Inherited,
|
||||
}
|
||||
|
||||
impl From<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 })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ mod kw {
|
|||
}
|
||||
|
||||
custom_keyword!(__evaluated_cfgs);
|
||||
custom_keyword!(add_platform_io);
|
||||
custom_keyword!(all);
|
||||
custom_keyword!(any);
|
||||
custom_keyword!(cfg);
|
||||
|
|
@ -75,6 +76,7 @@ mod kw {
|
|||
custom_keyword!(connect_inexact);
|
||||
custom_keyword!(custom_bounds);
|
||||
custom_keyword!(flip);
|
||||
custom_keyword!(get);
|
||||
custom_keyword!(hdl);
|
||||
custom_keyword!(hdl_module);
|
||||
custom_keyword!(incomplete_wire);
|
||||
|
|
@ -885,7 +887,13 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
|
|||
}
|
||||
}
|
||||
let _print_on_panic = PrintOnPanic(&contents);
|
||||
let contents = prettyplease::unparse(&parse_quote! { #contents });
|
||||
let mut parse_err = None;
|
||||
let (Ok(contents) | Err(contents)) = syn::parse2(contents.clone())
|
||||
.map(|file| prettyplease::unparse(&file))
|
||||
.map_err(|e| {
|
||||
parse_err = Some(e);
|
||||
contents.to_string()
|
||||
});
|
||||
let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
|
||||
let hash = base16ct::HexDisplay(&hash[..5]);
|
||||
file.write_all(contents.as_bytes()).unwrap();
|
||||
|
|
@ -897,9 +905,26 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
|
|||
e.unwrap();
|
||||
}
|
||||
}
|
||||
eprintln!("generated {}", dest_file.display());
|
||||
let log_msg = if let Some(parse_err) = parse_err {
|
||||
format!(
|
||||
"fayalite-proc-macros-impl internal error:\nfailed to parse generated output: {parse_err}\nunformatted output is in: {}\n",
|
||||
dest_file.display()
|
||||
)
|
||||
} else {
|
||||
format!("generated {}\n", dest_file.display())
|
||||
};
|
||||
// write message atomically if possible
|
||||
let mut stderr = std::io::stderr().lock();
|
||||
let write_result = stderr.write_all(log_msg.as_bytes());
|
||||
let flush_result = stderr.flush();
|
||||
drop(stderr); // unlock before we try to panic
|
||||
write_result.unwrap();
|
||||
flush_result.unwrap();
|
||||
std::io::stderr()
|
||||
.lock()
|
||||
.write_all(log_msg.as_bytes())
|
||||
.unwrap();
|
||||
let dest_file = dest_file.to_str().unwrap();
|
||||
|
||||
quote! {
|
||||
include!(#dest_file);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
Errors, HdlAttr, PairsIterExt,
|
||||
hdl_type_common::{ParsedGenerics, SplitForImpl},
|
||||
kw,
|
||||
module::transform_body::{HdlLet, HdlLetKindIO},
|
||||
module::transform_body::{HdlLet, HdlLetKindIO, ModuleIOOrAddPlatformIO},
|
||||
options,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
|
|
@ -39,7 +39,7 @@ pub(crate) fn check_name_conflicts_with_module_builder(name: &Ident) -> syn::Res
|
|||
if name == "m" {
|
||||
Err(Error::new_spanned(
|
||||
name,
|
||||
"name conflicts with implicit `m: &mut ModuleBuilder<_>`",
|
||||
"name conflicts with implicit `m: &ModuleBuilder`",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
|
|
@ -67,7 +67,7 @@ struct ModuleFnModule {
|
|||
vis: Visibility,
|
||||
sig: Signature,
|
||||
block: Box<Block>,
|
||||
struct_generics: ParsedGenerics,
|
||||
struct_generics: Option<ParsedGenerics>,
|
||||
the_struct: TokenStream,
|
||||
}
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ impl ModuleFn {
|
|||
paren_token,
|
||||
body,
|
||||
} => {
|
||||
debug_assert!(io.is_empty());
|
||||
debug_assert!(matches!(io, ModuleIOOrAddPlatformIO::ModuleIO(v) if v.is_empty()));
|
||||
return Ok(Self(ModuleFnImpl::Fn {
|
||||
attrs,
|
||||
config_options: HdlAttr {
|
||||
|
|
@ -322,6 +322,21 @@ impl ModuleFn {
|
|||
body,
|
||||
},
|
||||
};
|
||||
let io = match io {
|
||||
ModuleIOOrAddPlatformIO::ModuleIO(io) => io,
|
||||
ModuleIOOrAddPlatformIO::AddPlatformIO => {
|
||||
return Ok(Self(ModuleFnImpl::Module(ModuleFnModule {
|
||||
attrs,
|
||||
config_options,
|
||||
module_kind: module_kind.unwrap(),
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
struct_generics: None,
|
||||
the_struct: TokenStream::new(),
|
||||
})));
|
||||
}
|
||||
};
|
||||
let (_struct_impl_generics, _struct_type_generics, struct_where_clause) =
|
||||
struct_generics.split_for_impl();
|
||||
let struct_where_clause: Option<WhereClause> = parse_quote! { #struct_where_clause };
|
||||
|
|
@ -364,7 +379,7 @@ impl ModuleFn {
|
|||
vis,
|
||||
sig,
|
||||
block,
|
||||
struct_generics,
|
||||
struct_generics: Some(struct_generics),
|
||||
the_struct,
|
||||
})))
|
||||
}
|
||||
|
|
@ -433,9 +448,14 @@ impl ModuleFn {
|
|||
ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal },
|
||||
};
|
||||
let fn_name = &outer_sig.ident;
|
||||
let struct_ty = match struct_generics {
|
||||
Some(struct_generics) => {
|
||||
let (_struct_impl_generics, struct_type_generics, _struct_where_clause) =
|
||||
struct_generics.split_for_impl();
|
||||
let struct_ty = quote! {#fn_name #struct_type_generics};
|
||||
quote! {#fn_name #struct_type_generics}
|
||||
}
|
||||
None => quote! {::fayalite::bundle::Bundle},
|
||||
};
|
||||
body_sig.ident = parse_quote! {__body};
|
||||
body_sig
|
||||
.inputs
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ options! {
|
|||
pub(crate) enum LetFnKind {
|
||||
Input(input),
|
||||
Output(output),
|
||||
AddPlatformIO(add_platform_io),
|
||||
Instance(instance),
|
||||
RegBuilder(reg_builder),
|
||||
Wire(wire),
|
||||
|
|
@ -216,6 +217,49 @@ impl HdlLetKindToTokens for HdlLetKindInstance {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct HdlLetKindAddPlatformIO {
|
||||
pub(crate) m: kw::m,
|
||||
pub(crate) dot_token: Token![.],
|
||||
pub(crate) add_platform_io: kw::add_platform_io,
|
||||
pub(crate) paren: Paren,
|
||||
pub(crate) platform_io_builder: Box<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)]
|
||||
pub(crate) struct RegBuilderClockDomain {
|
||||
pub(crate) dot_token: Token![.],
|
||||
|
|
@ -711,6 +755,7 @@ impl HdlLetKindMemory {
|
|||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum HdlLetKind<IOType = ParsedType> {
|
||||
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
||||
AddPlatformIO(HdlLetKindAddPlatformIO),
|
||||
Incomplete(HdlLetKindIncomplete),
|
||||
Instance(HdlLetKindInstance),
|
||||
RegBuilder(HdlLetKindRegBuilder),
|
||||
|
|
@ -721,6 +766,7 @@ pub(crate) enum HdlLetKind<IOType = ParsedType> {
|
|||
impl_fold! {
|
||||
enum HdlLetKind<IOType,> {
|
||||
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
||||
AddPlatformIO(HdlLetKindAddPlatformIO),
|
||||
Incomplete(HdlLetKindIncomplete),
|
||||
Instance(HdlLetKindInstance),
|
||||
RegBuilder(HdlLetKindRegBuilder),
|
||||
|
|
@ -736,6 +782,9 @@ impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> {
|
|||
) -> Result<Self, ParseFailed> {
|
||||
match input {
|
||||
HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO),
|
||||
HdlLetKind::AddPlatformIO(input) => {
|
||||
ParseTypes::parse_types(input, parser).map(HdlLetKind::AddPlatformIO)
|
||||
}
|
||||
HdlLetKind::Incomplete(input) => {
|
||||
ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete)
|
||||
}
|
||||
|
|
@ -861,6 +910,23 @@ impl HdlLetKindParse for HdlLetKind<Type> {
|
|||
ModuleIOKind::Output(output),
|
||||
)
|
||||
.map(Self::IO),
|
||||
LetFnKind::AddPlatformIO((add_platform_io,)) => {
|
||||
if let Some(parsed_ty) = parsed_ty {
|
||||
return Err(Error::new_spanned(
|
||||
parsed_ty.1,
|
||||
"type annotation not allowed for instance",
|
||||
));
|
||||
}
|
||||
let (m, dot_token) = unwrap_m_dot(m_dot, kind)?;
|
||||
let paren_contents;
|
||||
Ok(Self::AddPlatformIO(HdlLetKindAddPlatformIO {
|
||||
m,
|
||||
dot_token,
|
||||
add_platform_io,
|
||||
paren: parenthesized!(paren_contents in input),
|
||||
platform_io_builder: paren_contents.call(parse_single_fn_arg)?,
|
||||
}))
|
||||
}
|
||||
LetFnKind::Instance((instance,)) => {
|
||||
if let Some(parsed_ty) = parsed_ty {
|
||||
return Err(Error::new_spanned(
|
||||
|
|
@ -936,6 +1002,7 @@ impl HdlLetKindToTokens for HdlLetKind {
|
|||
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
HdlLetKind::IO(v) => v.ty_to_tokens(tokens),
|
||||
HdlLetKind::AddPlatformIO(v) => v.ty_to_tokens(tokens),
|
||||
HdlLetKind::Incomplete(v) => v.ty_to_tokens(tokens),
|
||||
HdlLetKind::Instance(v) => v.ty_to_tokens(tokens),
|
||||
HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens),
|
||||
|
|
@ -947,6 +1014,7 @@ impl HdlLetKindToTokens for HdlLetKind {
|
|||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
HdlLetKind::IO(v) => v.expr_to_tokens(tokens),
|
||||
HdlLetKind::AddPlatformIO(v) => v.expr_to_tokens(tokens),
|
||||
HdlLetKind::Incomplete(v) => v.expr_to_tokens(tokens),
|
||||
HdlLetKind::Instance(v) => v.expr_to_tokens(tokens),
|
||||
HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens),
|
||||
|
|
@ -1149,7 +1217,7 @@ impl<T: ToString> ToTokens for ImplicitName<T> {
|
|||
struct Visitor<'a> {
|
||||
module_kind: Option<ModuleKind>,
|
||||
errors: Errors,
|
||||
io: Vec<ModuleIO>,
|
||||
io: ModuleIOOrAddPlatformIO,
|
||||
block_depth: usize,
|
||||
parsed_generics: &'a ParsedGenerics,
|
||||
}
|
||||
|
|
@ -1289,7 +1357,81 @@ impl Visitor<'_> {
|
|||
}),
|
||||
semi_token: hdl_let.semi_token,
|
||||
};
|
||||
self.io.push(hdl_let);
|
||||
match &mut self.io {
|
||||
ModuleIOOrAddPlatformIO::ModuleIO(io) => io.push(hdl_let),
|
||||
ModuleIOOrAddPlatformIO::AddPlatformIO => {
|
||||
self.errors.error(
|
||||
kind,
|
||||
"can't have other inputs/outputs in a module using m.add_platform_io()",
|
||||
);
|
||||
}
|
||||
}
|
||||
let_stmt
|
||||
}
|
||||
fn process_hdl_let_add_platform_io(
|
||||
&mut self,
|
||||
hdl_let: HdlLet<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
|
||||
}
|
||||
fn process_hdl_let_instance(&mut self, hdl_let: HdlLet<HdlLetKindInstance>) -> Local {
|
||||
|
|
@ -1510,6 +1652,7 @@ impl Visitor<'_> {
|
|||
}
|
||||
the_match! {
|
||||
IO => process_hdl_let_io,
|
||||
AddPlatformIO => process_hdl_let_add_platform_io,
|
||||
Incomplete => process_hdl_let_incomplete,
|
||||
Instance => process_hdl_let_instance,
|
||||
RegBuilder => process_hdl_let_reg_builder,
|
||||
|
|
@ -1753,15 +1896,20 @@ impl Fold for Visitor<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) enum ModuleIOOrAddPlatformIO {
|
||||
ModuleIO(Vec<ModuleIO>),
|
||||
AddPlatformIO,
|
||||
}
|
||||
|
||||
pub(crate) fn transform_body(
|
||||
module_kind: Option<ModuleKind>,
|
||||
mut body: Box<Block>,
|
||||
parsed_generics: &ParsedGenerics,
|
||||
) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {
|
||||
) -> syn::Result<(Box<Block>, ModuleIOOrAddPlatformIO)> {
|
||||
let mut visitor = Visitor {
|
||||
module_kind,
|
||||
errors: Errors::new(),
|
||||
io: vec![],
|
||||
io: ModuleIOOrAddPlatformIO::ModuleIO(vec![]),
|
||||
block_depth: 0,
|
||||
parsed_generics,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@ impl Visitor<'_> {
|
|||
field.expr = parse_quote_spanned! {field.member.span()=>
|
||||
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
|
||||
};
|
||||
field
|
||||
.colon_token
|
||||
.get_or_insert(Token));
|
||||
}
|
||||
return parse_quote_spanned! {name_span=>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned};
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::BTreeMap;
|
||||
use syn::{
|
||||
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
|
||||
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
|
||||
|
|
@ -24,65 +24,65 @@ use syn::{
|
|||
|
||||
macro_rules! visit_trait {
|
||||
(
|
||||
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)*
|
||||
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &mut $Value:ty) $block:block)*
|
||||
) => {
|
||||
trait VisitMatchPat<'a> {
|
||||
$(fn $fn(&mut self, $value: &'a $Value) {
|
||||
$(fn $fn(&mut self, $value: &'a mut $Value) {
|
||||
$fn(self, $value);
|
||||
})*
|
||||
}
|
||||
|
||||
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)*
|
||||
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a mut $Value) $block)*
|
||||
};
|
||||
}
|
||||
|
||||
visit_trait! {
|
||||
fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) {
|
||||
let MatchPatBinding { ident: _ } = v;
|
||||
fn visit_match_pat_binding(_state: _, v: &mut MatchPatBinding) {
|
||||
let MatchPatBinding { mutability: _, ident: _ } = v;
|
||||
}
|
||||
fn visit_match_pat_wild(_state: _, v: &MatchPatWild) {
|
||||
fn visit_match_pat_wild(_state: _, v: &mut MatchPatWild) {
|
||||
let MatchPatWild { underscore_token: _ } = v;
|
||||
}
|
||||
fn visit_match_pat_rest(_state: _, v: &MatchPatRest) {
|
||||
fn visit_match_pat_rest(_state: _, v: &mut MatchPatRest) {
|
||||
let MatchPatRest { dot2_token: _ } = v;
|
||||
}
|
||||
fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) {
|
||||
fn visit_match_pat_paren(state: _, v: &mut MatchPatParen<MatchPat>) {
|
||||
let MatchPatParen { paren_token: _, pat } = v;
|
||||
state.visit_match_pat(pat);
|
||||
}
|
||||
fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) {
|
||||
fn visit_match_pat_paren_simple(state: _, v: &mut MatchPatParen<MatchPatSimple>) {
|
||||
let MatchPatParen { paren_token: _, pat } = v;
|
||||
state.visit_match_pat_simple(pat);
|
||||
}
|
||||
fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) {
|
||||
fn visit_match_pat_or(state: _, v: &mut MatchPatOr<MatchPat>) {
|
||||
let MatchPatOr { leading_vert: _, cases } = v;
|
||||
for v in cases {
|
||||
state.visit_match_pat(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) {
|
||||
fn visit_match_pat_or_simple(state: _, v: &mut MatchPatOr<MatchPatSimple>) {
|
||||
let MatchPatOr { leading_vert: _, cases } = v;
|
||||
for v in cases {
|
||||
state.visit_match_pat_simple(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) {
|
||||
fn visit_match_pat_struct_field(state: _, v: &mut MatchPatStructField) {
|
||||
let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
|
||||
state.visit_match_pat_simple(pat);
|
||||
}
|
||||
fn visit_match_pat_struct(state: _, v: &MatchPatStruct) {
|
||||
fn visit_match_pat_struct(state: _, v: &mut MatchPatStruct) {
|
||||
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
|
||||
for v in fields {
|
||||
state.visit_match_pat_struct_field(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) {
|
||||
fn visit_match_pat_tuple(state: _, v: &mut MatchPatTuple) {
|
||||
let MatchPatTuple { paren_token: _, fields } = v;
|
||||
for v in fields {
|
||||
state.visit_match_pat_simple(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) {
|
||||
fn visit_match_pat_enum_variant(state: _, v: &mut MatchPatEnumVariant) {
|
||||
let MatchPatEnumVariant {
|
||||
match_span:_,
|
||||
sim:_,
|
||||
|
|
@ -95,7 +95,7 @@ visit_trait! {
|
|||
state.visit_match_pat_simple(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_simple(state: _, v: &MatchPatSimple) {
|
||||
fn visit_match_pat_simple(state: _, v: &mut MatchPatSimple) {
|
||||
match v {
|
||||
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
|
||||
MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v),
|
||||
|
|
@ -104,7 +104,7 @@ visit_trait! {
|
|||
MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v),
|
||||
}
|
||||
}
|
||||
fn visit_match_pat(state: _, v: &MatchPat) {
|
||||
fn visit_match_pat(state: _, v: &mut MatchPat) {
|
||||
match v {
|
||||
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
|
||||
MatchPat::Or(v) => state.visit_match_pat_or(v),
|
||||
|
|
@ -118,13 +118,15 @@ visit_trait! {
|
|||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatBinding<> {
|
||||
mutability: Option<Token![mut]>,
|
||||
ident: Ident,
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatBinding {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self { ident } = self;
|
||||
let Self { mutability, ident } = self;
|
||||
mutability.to_tokens(tokens);
|
||||
ident.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
|
@ -211,12 +213,20 @@ impl ToTokens for MatchPatStructField {
|
|||
colon_token,
|
||||
pat,
|
||||
} = self;
|
||||
field_name.to_tokens(tokens);
|
||||
if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
|
||||
if let (
|
||||
None,
|
||||
MatchPatSimple::Binding(MatchPatBinding {
|
||||
mutability: _,
|
||||
ident,
|
||||
}),
|
||||
) = (colon_token, pat)
|
||||
{
|
||||
if field_name == ident {
|
||||
pat.to_tokens(tokens);
|
||||
return;
|
||||
}
|
||||
}
|
||||
field_name.to_tokens(tokens);
|
||||
colon_token
|
||||
.unwrap_or_else(|| Token))
|
||||
.to_tokens(tokens);
|
||||
|
|
@ -450,7 +460,7 @@ trait ParseMatchPat: Sized {
|
|||
Pat::Ident(PatIdent {
|
||||
attrs: _,
|
||||
by_ref,
|
||||
mutability,
|
||||
mut mutability,
|
||||
ident,
|
||||
subpat,
|
||||
}) => {
|
||||
|
|
@ -459,10 +469,13 @@ trait ParseMatchPat: Sized {
|
|||
.errors
|
||||
.error(by_ref, "ref not allowed in #[hdl] patterns");
|
||||
}
|
||||
if let Some(mutability) = mutability {
|
||||
if let Some(mut_token) = mutability {
|
||||
if state.sim.is_none() {
|
||||
state
|
||||
.errors
|
||||
.error(mutability, "mut not allowed in #[hdl] patterns");
|
||||
.error(mut_token, "mut not allowed in #[hdl] patterns");
|
||||
mutability = None; // avoid duplicate errors
|
||||
}
|
||||
}
|
||||
if let Some((at_token, _)) = subpat {
|
||||
state
|
||||
|
|
@ -474,7 +487,13 @@ trait ParseMatchPat: Sized {
|
|||
variant_path,
|
||||
enum_path,
|
||||
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,
|
||||
MatchPatEnumVariant {
|
||||
match_span: state.match_span,
|
||||
|
|
@ -484,8 +503,10 @@ trait ParseMatchPat: Sized {
|
|||
variant_name,
|
||||
field: None,
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
|
||||
mutability,
|
||||
ident,
|
||||
}))),
|
||||
}
|
||||
|
|
@ -980,15 +1001,16 @@ struct HdlMatchParseState<'a> {
|
|||
|
||||
struct HdlLetPatVisitState<'a> {
|
||||
errors: &'a mut Errors,
|
||||
bindings: BTreeSet<&'a Ident>,
|
||||
bindings: BTreeMap<Ident, MatchPatBinding>,
|
||||
}
|
||||
|
||||
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
||||
fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) {
|
||||
self.bindings.insert(&v.ident);
|
||||
fn visit_match_pat_binding(&mut self, v: &'a mut MatchPatBinding) {
|
||||
self.bindings.insert(v.ident.clone(), v.clone());
|
||||
v.mutability = None;
|
||||
}
|
||||
|
||||
fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) {
|
||||
fn visit_match_pat_or(&mut self, v: &'a mut MatchPatOr<MatchPat>) {
|
||||
if let Some(first_inner_vert) = v.first_inner_vert() {
|
||||
self.errors.error(
|
||||
first_inner_vert,
|
||||
|
|
@ -998,7 +1020,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
|||
visit_match_pat_or(self, v);
|
||||
}
|
||||
|
||||
fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) {
|
||||
fn visit_match_pat_or_simple(&mut self, v: &'a mut MatchPatOr<MatchPatSimple>) {
|
||||
if let Some(first_inner_vert) = v.first_inner_vert() {
|
||||
self.errors.error(
|
||||
first_inner_vert,
|
||||
|
|
@ -1008,7 +1030,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
|||
visit_match_pat_or_simple(self, v);
|
||||
}
|
||||
|
||||
fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) {
|
||||
fn visit_match_pat_enum_variant(&mut self, v: &'a mut MatchPatEnumVariant) {
|
||||
self.errors.error(v, "refutable pattern in let statement");
|
||||
}
|
||||
}
|
||||
|
|
@ -1048,7 +1070,7 @@ impl Visitor<'_> {
|
|||
.error(else_, "#[hdl] let ... else { ... } is not implemented");
|
||||
return empty_let();
|
||||
}
|
||||
let Ok(pat) = MatchPat::parse(
|
||||
let Ok(mut pat) = MatchPat::parse(
|
||||
&mut HdlMatchParseState {
|
||||
sim,
|
||||
match_span: span,
|
||||
|
|
@ -1060,20 +1082,27 @@ impl Visitor<'_> {
|
|||
};
|
||||
let mut state = HdlLetPatVisitState {
|
||||
errors: &mut self.errors,
|
||||
bindings: BTreeSet::new(),
|
||||
bindings: BTreeMap::new(),
|
||||
};
|
||||
state.visit_match_pat(&pat);
|
||||
state.visit_match_pat(&mut pat);
|
||||
let HdlLetPatVisitState {
|
||||
errors: _,
|
||||
bindings,
|
||||
} = state;
|
||||
let bindings_idents = bindings.keys();
|
||||
let bindings = bindings.values();
|
||||
let retval = if sim.is_some() {
|
||||
parse_quote_spanned! {span=>
|
||||
let (#(#bindings,)*) = {
|
||||
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
||||
let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
|
||||
#let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token
|
||||
(#(#bindings,)*)
|
||||
let __match_value = #expr;
|
||||
let __match_value = {
|
||||
use ::fayalite::sim::value::match_sim_value::*;
|
||||
// use method syntax to deduce the correct trait to call
|
||||
::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value()
|
||||
};
|
||||
#let_token #pat #eq_token __match_value #semi_token
|
||||
(#(#bindings_idents,)*)
|
||||
};
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1105,7 +1134,7 @@ impl Visitor<'_> {
|
|||
__match_variant,
|
||||
);
|
||||
#let_token #pat #eq_token __match_variant #semi_token
|
||||
(#(#bindings,)* __scope,)
|
||||
(#(#bindings_idents,)* __scope,)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -1142,8 +1171,13 @@ impl Visitor<'_> {
|
|||
quote_spanned! {span=>
|
||||
{
|
||||
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
||||
let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
|
||||
#match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) {
|
||||
let __match_value = #expr;
|
||||
let __match_value = {
|
||||
use ::fayalite::sim::value::match_sim_value::*;
|
||||
// use method syntax to deduce the correct trait to call
|
||||
::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value()
|
||||
};
|
||||
#match_token __match_value {
|
||||
#(#arms)*
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@ rust-version.workspace = true
|
|||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
base64.workspace = true
|
||||
bitvec.workspace = true
|
||||
blake3.workspace = true
|
||||
clap.workspace = true
|
||||
clap_complete.workspace = true
|
||||
ctor.workspace = true
|
||||
eyre.workspace = true
|
||||
fayalite-proc-macros.workspace = true
|
||||
|
|
@ -24,7 +26,7 @@ hashbrown.workspace = true
|
|||
jobslot.workspace = true
|
||||
num-bigint.workspace = true
|
||||
num-traits.workspace = true
|
||||
os_pipe.workspace = true
|
||||
ordered-float.workspace = true
|
||||
petgraph.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,47 +1,64 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use clap::Parser;
|
||||
use fayalite::{cli, prelude::*};
|
||||
use fayalite::prelude::*;
|
||||
|
||||
#[hdl_module]
|
||||
fn blinky(clock_frequency: u64) {
|
||||
#[hdl]
|
||||
let clk: Clock = m.input();
|
||||
#[hdl]
|
||||
let rst: SyncReset = m.input();
|
||||
fn blinky(platform_io_builder: PlatformIOBuilder<'_>) {
|
||||
let clk_input =
|
||||
platform_io_builder.peripherals_with_type::<peripherals::ClockInput>()[0].use_peripheral();
|
||||
let rst = platform_io_builder.peripherals_with_type::<Reset>()[0].use_peripheral();
|
||||
let cd = #[hdl]
|
||||
ClockDomain {
|
||||
clk,
|
||||
rst: rst.to_reset(),
|
||||
clk: clk_input.clk,
|
||||
rst,
|
||||
};
|
||||
let max_value = clock_frequency / 2 - 1;
|
||||
let max_value = (clk_input.ty().frequency() / 2.0).round_ties_even() as u64 - 1;
|
||||
let int_ty = UInt::range_inclusive(0..=max_value);
|
||||
#[hdl]
|
||||
let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty));
|
||||
#[hdl]
|
||||
let output_reg: Bool = reg_builder().clock_domain(cd).reset(false);
|
||||
#[hdl]
|
||||
let rgb_output_reg = reg_builder().clock_domain(cd).reset(
|
||||
#[hdl]
|
||||
peripherals::RgbLed {
|
||||
r: false,
|
||||
g: false,
|
||||
b: false,
|
||||
},
|
||||
);
|
||||
#[hdl]
|
||||
if counter_reg.cmp_eq(max_value) {
|
||||
connect_any(counter_reg, 0u8);
|
||||
connect(output_reg, !output_reg);
|
||||
connect(rgb_output_reg.r, !rgb_output_reg.r);
|
||||
#[hdl]
|
||||
if rgb_output_reg.r {
|
||||
connect(rgb_output_reg.g, !rgb_output_reg.g);
|
||||
#[hdl]
|
||||
if rgb_output_reg.g {
|
||||
connect(rgb_output_reg.b, !rgb_output_reg.b);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
connect_any(counter_reg, counter_reg + 1_hdl_u1);
|
||||
}
|
||||
for led in platform_io_builder.peripherals_with_type::<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]
|
||||
let led: Bool = m.output();
|
||||
connect(led, output_reg);
|
||||
let io = m.add_platform_io(platform_io_builder);
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
/// clock frequency in hertz
|
||||
#[arg(long, default_value = "1000000", value_parser = clap::value_parser!(u64).range(2..))]
|
||||
clock_frequency: u64,
|
||||
#[command(subcommand)]
|
||||
cli: cli::Cli,
|
||||
}
|
||||
|
||||
fn main() -> cli::Result {
|
||||
let cli = Cli::parse();
|
||||
cli.cli.run(blinky(cli.clock_frequency))
|
||||
fn main() {
|
||||
<BuildCli>::main("blinky", |_, platform, _| {
|
||||
Ok(JobParams::new(platform.wrap_main_module(blinky)))
|
||||
});
|
||||
}
|
||||
|
|
|
|||
188
crates/fayalite/examples/tx_only_uart.rs
Normal file
188
crates/fayalite/examples/tx_only_uart.rs
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use clap::builder::TypedValueParser;
|
||||
use fayalite::{
|
||||
build::{ToArgs, WriteArgs},
|
||||
platform::PeripheralRef,
|
||||
prelude::*,
|
||||
};
|
||||
use ordered_float::NotNan;
|
||||
|
||||
fn pick_clock<'a>(
|
||||
platform_io_builder: &PlatformIOBuilder<'a>,
|
||||
) -> PeripheralRef<'a, peripherals::ClockInput> {
|
||||
let mut clks = platform_io_builder.peripherals_with_type::<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,3 +31,81 @@
|
|||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! You can also use `#[hdl(sim)] let` to destructure [`SimValue`]s (or anything that implements [`ToSimValue`]).
|
||||
//!
|
||||
//! [`SimValue`]: crate::sim::value::SimValue
|
||||
//! [`ToSimValue`]: crate::sim::value::ToSimValue
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! #[hdl]
|
||||
//! struct MyStruct<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_to_sim_value<'a, T: Type>(v: impl ToSimValue<Type = MyStruct<T>>) {
|
||||
//! #[hdl(sim)]
|
||||
//! let MyStruct::<T> {
|
||||
//! a,
|
||||
//! b,
|
||||
//! c,
|
||||
//! } = v;
|
||||
//!
|
||||
//! // that gives these types:
|
||||
//! let _: SimValue<UInt<8>> = a;
|
||||
//! let _: SimValue<Bool> = b;
|
||||
//! let _: SimValue<T> = c;
|
||||
//! }
|
||||
//! ```
|
||||
|
|
|
|||
|
|
@ -9,3 +9,78 @@
|
|||
//!
|
||||
//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now,
|
||||
//! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
|
||||
//!
|
||||
//! You can also use `#[hdl(sim)] match` to match [`SimValue`]s (or anything that implements [`ToSimValue`]).
|
||||
//!
|
||||
//! `#[hdl(sim)] match` statements' bodies may have any type, unlike `#[hdl] match`.
|
||||
//!
|
||||
//! [`SimValue`]: crate::sim::value::SimValue
|
||||
//! [`ToSimValue`]: crate::sim::value::ToSimValue
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! #[hdl]
|
||||
//! enum MyEnum<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_to_sim_value<'a, T: Type>(v: impl ToSimValue<Type = MyEnum<T>>) {
|
||||
//! #[hdl(sim)]
|
||||
//! match v {
|
||||
//! MyEnum::<T>::A => println!("got A"),
|
||||
//! MyEnum::<T>::B(b) => {
|
||||
//! let _: SimValue<Bool> = b; // b has this type
|
||||
//! println!("got B({b})");
|
||||
//! }
|
||||
//! _ => println!("something else"),
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
|
|
|||
|
|
@ -145,52 +145,73 @@ pub struct DocStringAnnotation {
|
|||
|
||||
macro_rules! make_annotation_enum {
|
||||
(
|
||||
#[$non_exhaustive:ident]
|
||||
$(#[$meta:meta])*
|
||||
$vis:vis enum $Annotation:ident {
|
||||
$($Variant:ident($T:ident),)*
|
||||
$vis:vis enum $AnnotationEnum:ident {
|
||||
$($Variant:ident($T:ty),)*
|
||||
}
|
||||
) => {
|
||||
crate::annotations::make_annotation_enum!(@require_non_exhaustive $non_exhaustive);
|
||||
|
||||
#[$non_exhaustive]
|
||||
$(#[$meta])*
|
||||
$vis enum $Annotation {
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
$vis enum $AnnotationEnum {
|
||||
$($Variant($T),)*
|
||||
}
|
||||
|
||||
$(impl IntoAnnotations for $T {
|
||||
type IntoAnnotations = [$Annotation; 1];
|
||||
impl std::fmt::Debug for $AnnotationEnum {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.fmt(f),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(impl 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 {
|
||||
[$Annotation::$Variant(self)]
|
||||
[self.into()]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAnnotations for &'_ $T {
|
||||
type IntoAnnotations = [$Annotation; 1];
|
||||
impl crate::annotations::IntoAnnotations for &'_ $T {
|
||||
type IntoAnnotations = [crate::annotations::Annotation; 1];
|
||||
|
||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||
[$Annotation::$Variant(*self)]
|
||||
[crate::annotations::Annotation::from(self.clone())]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAnnotations for &'_ mut $T {
|
||||
type IntoAnnotations = [$Annotation; 1];
|
||||
impl crate::annotations::IntoAnnotations for &'_ mut $T {
|
||||
type IntoAnnotations = [crate::annotations::Annotation; 1];
|
||||
|
||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||
[$Annotation::$Variant(*self)]
|
||||
[crate::annotations::Annotation::from(self.clone())]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAnnotations for Box<$T> {
|
||||
type IntoAnnotations = [$Annotation; 1];
|
||||
impl crate::annotations::IntoAnnotations for Box<$T> {
|
||||
type IntoAnnotations = [crate::annotations::Annotation; 1];
|
||||
|
||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||
[$Annotation::$Variant(*self)]
|
||||
[crate::annotations::Annotation::from(*self)]
|
||||
}
|
||||
})*
|
||||
};
|
||||
(@require_non_exhaustive non_exhaustive) => {};
|
||||
}
|
||||
|
||||
pub(crate) use make_annotation_enum;
|
||||
|
||||
make_annotation_enum! {
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Annotation {
|
||||
DontTouch(DontTouchAnnotation),
|
||||
|
|
@ -199,6 +220,7 @@ make_annotation_enum! {
|
|||
BlackBoxPath(BlackBoxPathAnnotation),
|
||||
DocString(DocStringAnnotation),
|
||||
CustomFirrtl(CustomFirrtlAnnotation),
|
||||
Xilinx(crate::vendor::xilinx::XilinxAnnotation),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
|
||||
use crate::{
|
||||
expr::{
|
||||
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
|
||||
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq},
|
||||
CastToBits, Expr, HdlPartialEq, HdlPartialEqImpl, ReduceBits, ToExpr, ValueType, Valueless,
|
||||
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator},
|
||||
},
|
||||
int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType},
|
||||
intern::{Intern, Interned, LazyInterned},
|
||||
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
sim::value::{SimValue, SimValuePartialEq},
|
||||
sim::value::SimValue,
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
||||
|
|
@ -19,7 +19,7 @@ use crate::{
|
|||
util::ConstUsize,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
|
||||
use std::{iter::FusedIterator, ops::Index};
|
||||
use std::{borrow::Cow, iter::FusedIterator, ops::Index};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
||||
|
|
@ -221,7 +221,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
|||
let value = AsMut::<[SimValue<T>]>::as_mut(value);
|
||||
assert_eq!(self.len(), value.len());
|
||||
for element_value in value {
|
||||
assert_eq!(SimValue::ty(element_value), element_ty);
|
||||
assert_eq!(element_value.ty(), element_ty);
|
||||
let (element_opaque, rest) = opaque.split_at(element_size);
|
||||
SimValue::opaque_mut(element_value).clone_from_slice(element_opaque);
|
||||
opaque = rest;
|
||||
|
|
@ -238,7 +238,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
|||
let value = AsRef::<[SimValue<T>]>::as_ref(value);
|
||||
assert_eq!(self.len(), value.len());
|
||||
for element_value in value {
|
||||
assert_eq!(SimValue::ty(element_value), element_ty);
|
||||
assert_eq!(element_value.ty(), element_ty);
|
||||
writer.fill_prefix_with(element_size, |writer| {
|
||||
writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice())
|
||||
});
|
||||
|
|
@ -326,14 +326,30 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
||||
impl<Lhs: Type, Rhs: Type, Len: Size> HdlPartialEqImpl<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
||||
where
|
||||
Lhs: ExprPartialEq<Rhs>,
|
||||
Lhs: HdlPartialEqImpl<Rhs>,
|
||||
{
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
let lhs_ty = Expr::ty(lhs);
|
||||
let rhs_ty = Expr::ty(rhs);
|
||||
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||
fn cmp_value_eq(
|
||||
lhs: Self,
|
||||
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());
|
||||
lhs.into_iter()
|
||||
.zip(rhs)
|
||||
.map(|(l, r)| l.cmp_eq(r))
|
||||
|
|
@ -341,11 +357,8 @@ where
|
|||
.cast_to_bits()
|
||||
.all_one_bits()
|
||||
}
|
||||
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
let lhs_ty = Expr::ty(lhs);
|
||||
let rhs_ty = Expr::ty(rhs);
|
||||
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
||||
lhs.into_iter()
|
||||
.zip(rhs)
|
||||
.map(|(l, r)| l.cmp_ne(r))
|
||||
|
|
@ -353,17 +366,19 @@ where
|
|||
.cast_to_bits()
|
||||
.any_one_bits()
|
||||
}
|
||||
fn cmp_valueless_eq(
|
||||
lhs: Valueless<Self>,
|
||||
rhs: Valueless<ArrayType<Rhs, Len>>,
|
||||
) -> Valueless<Bool> {
|
||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
||||
Valueless::new(Bool)
|
||||
}
|
||||
|
||||
impl<Lhs: Type, Rhs: Type, Len: Size> SimValuePartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
||||
where
|
||||
Lhs: SimValuePartialEq<Rhs>,
|
||||
{
|
||||
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<ArrayType<Rhs, Len>>) -> bool {
|
||||
AsRef::<[_]>::as_ref(&**this)
|
||||
.iter()
|
||||
.zip(AsRef::<[_]>::as_ref(&**other))
|
||||
.all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r))
|
||||
fn cmp_valueless_ne(
|
||||
lhs: Valueless<Self>,
|
||||
rhs: Valueless<ArrayType<Rhs, Len>>,
|
||||
) -> Valueless<Bool> {
|
||||
assert_eq!(lhs.ty().len(), rhs.ty().len());
|
||||
Valueless::new(Bool)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +389,7 @@ impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> {
|
|||
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter {
|
||||
ExprArrayIter {
|
||||
base: e,
|
||||
indexes: 0..Expr::ty(e).len(),
|
||||
indexes: 0..e.ty().len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2803
crates/fayalite/src/build.rs
Normal file
2803
crates/fayalite/src/build.rs
Normal file
File diff suppressed because it is too large
Load diff
1177
crates/fayalite/src/build/external.rs
Normal file
1177
crates/fayalite/src/build/external.rs
Normal file
File diff suppressed because it is too large
Load diff
128
crates/fayalite/src/build/firrtl.rs
Normal file
128
crates/fayalite/src/build/firrtl.rs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
build::{
|
||||
BaseJob, BaseJobKind, CommandParams, DynJobKind, GlobalParams, JobAndDependencies,
|
||||
JobArgsAndDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams,
|
||||
ToArgs, WriteArgs,
|
||||
},
|
||||
firrtl::{ExportOptions, FileBackend},
|
||||
intern::{Intern, InternSlice, Interned},
|
||||
util::job_server::AcquiredJob,
|
||||
};
|
||||
use clap::Args;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub struct FirrtlJobKind;
|
||||
|
||||
#[derive(Args, Debug, Clone, Hash, PartialEq, Eq)]
|
||||
#[group(id = "Firrtl")]
|
||||
#[non_exhaustive]
|
||||
pub struct FirrtlArgs {
|
||||
#[command(flatten)]
|
||||
pub export_options: ExportOptions,
|
||||
}
|
||||
|
||||
impl ToArgs for FirrtlArgs {
|
||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self { export_options } = self;
|
||||
export_options.to_args(args);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Firrtl {
|
||||
base: BaseJob,
|
||||
export_options: ExportOptions,
|
||||
}
|
||||
|
||||
impl Firrtl {
|
||||
fn make_firrtl_file_backend(&self) -> FileBackend {
|
||||
FileBackend {
|
||||
dir_path: PathBuf::from(&*self.base.output_dir()),
|
||||
top_fir_file_stem: Some(self.base.file_stem().into()),
|
||||
circuit_name: None,
|
||||
}
|
||||
}
|
||||
pub fn firrtl_file(&self) -> Interned<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)]
|
||||
}
|
||||
388
crates/fayalite/src/build/formal.rs
Normal file
388
crates/fayalite/src/build/formal.rs
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
build::{
|
||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams,
|
||||
JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
|
||||
JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
||||
external::{
|
||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
},
|
||||
verilog::{UnadjustedVerilog, VerilogDialect, VerilogJob, VerilogJobKind},
|
||||
},
|
||||
intern::{Intern, InternSlice, Interned},
|
||||
module::NameId,
|
||||
testing::FormalMode,
|
||||
util::job_server::AcquiredJob,
|
||||
};
|
||||
use clap::Args;
|
||||
use eyre::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
fmt::{self, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[derive(Args, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub struct FormalArgs {
|
||||
#[arg(long = "sby-extra-arg", value_name = "ARG")]
|
||||
pub sby_extra_args: Vec<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()),
|
||||
]
|
||||
}
|
||||
855
crates/fayalite/src/build/graph.rs
Normal file
855
crates/fayalite/src/build/graph.rs
Normal file
|
|
@ -0,0 +1,855 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
build::{
|
||||
DynJob, GlobalParams, JobItem, JobItemName, JobParams, program_name_for_internal_jobs,
|
||||
},
|
||||
intern::Interned,
|
||||
platform::DynPlatform,
|
||||
util::{HashMap, HashSet, job_server::AcquiredJob},
|
||||
};
|
||||
use eyre::{ContextCompat, eyre};
|
||||
use petgraph::{
|
||||
algo::{DfsSpace, kosaraju_scc, toposort},
|
||||
graph::DiGraph,
|
||||
visit::{GraphBase, Visitable},
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::SerializeSeq};
|
||||
use std::{
|
||||
cell::OnceCell,
|
||||
collections::{BTreeMap, BTreeSet, VecDeque},
|
||||
convert::Infallible,
|
||||
ffi::OsStr,
|
||||
fmt::{self, Write},
|
||||
panic,
|
||||
rc::Rc,
|
||||
str::Utf8Error,
|
||||
sync::mpsc,
|
||||
thread::{self, ScopedJoinHandle},
|
||||
};
|
||||
|
||||
macro_rules! write_str {
|
||||
($s:expr, $($rest:tt)*) => {
|
||||
write!($s, $($rest)*).expect("String::write_fmt can't fail")
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum JobGraphNode {
|
||||
Job(DynJob),
|
||||
Item {
|
||||
#[allow(dead_code, reason = "name used for debugging")]
|
||||
name: JobItemName,
|
||||
source_job: Option<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::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::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)
|
||||
}
|
||||
}
|
||||
313
crates/fayalite/src/build/registry.rs
Normal file
313
crates/fayalite/src/build/registry.rs
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
build::{DynJobKind, JobKind, built_in_job_kinds},
|
||||
intern::Interned,
|
||||
util::InternedStrCompareAsStr,
|
||||
};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fmt,
|
||||
sync::{Arc, OnceLock, RwLock, RwLockWriteGuard},
|
||||
};
|
||||
|
||||
impl DynJobKind {
|
||||
pub fn registry() -> JobKindRegistrySnapshot {
|
||||
JobKindRegistrySnapshot(JobKindRegistry::get())
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn register(self) {
|
||||
JobKindRegistry::register(JobKindRegistry::lock(), self);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct JobKindRegistry {
|
||||
job_kinds: BTreeMap<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();
|
||||
}
|
||||
418
crates/fayalite/src/build/verilog.rs
Normal file
418
crates/fayalite/src/build/verilog.rs
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
build::{
|
||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GetJobPositionJob,
|
||||
GlobalParams, JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem,
|
||||
JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
||||
external::{
|
||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
},
|
||||
firrtl::{Firrtl, FirrtlJobKind},
|
||||
},
|
||||
intern::{Intern, InternSlice, Interned},
|
||||
util::job_server::AcquiredJob,
|
||||
};
|
||||
use clap::Args;
|
||||
use eyre::{Context, bail};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
fmt, mem,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
/// based on [LLVM Circt's recommended lowering options][lowering-options]
|
||||
///
|
||||
/// [lowering-options]: https://circt.llvm.org/docs/VerilogGeneration/#recommended-loweringoptions-by-target
|
||||
#[derive(clap::ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum VerilogDialect {
|
||||
Questa,
|
||||
Spyglass,
|
||||
Verilator,
|
||||
Vivado,
|
||||
Yosys,
|
||||
}
|
||||
|
||||
impl fmt::Display for VerilogDialect {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl VerilogDialect {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
VerilogDialect::Questa => "questa",
|
||||
VerilogDialect::Spyglass => "spyglass",
|
||||
VerilogDialect::Verilator => "verilator",
|
||||
VerilogDialect::Vivado => "vivado",
|
||||
VerilogDialect::Yosys => "yosys",
|
||||
}
|
||||
}
|
||||
pub fn firtool_extra_args(self) -> &'static [&'static str] {
|
||||
match self {
|
||||
VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"],
|
||||
VerilogDialect::Spyglass => {
|
||||
&["--lowering-options=explicitBitcast,disallowExpressionInliningInPorts"]
|
||||
}
|
||||
VerilogDialect::Verilator => &[
|
||||
"--lowering-options=locationInfoStyle=wrapInAtSquareBracket,disallowLocalVariables",
|
||||
],
|
||||
VerilogDialect::Vivado => &["--lowering-options=mitigateVivadoArrayIndexConstPropBug"],
|
||||
VerilogDialect::Yosys => {
|
||||
&["--lowering-options=disallowLocalVariables,disallowPackedArrays"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub struct UnadjustedVerilogArgs {
|
||||
#[arg(long = "firtool-extra-arg", value_name = "ARG")]
|
||||
pub firtool_extra_args: Vec<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=all"]);
|
||||
}
|
||||
if let Some(dialect) = verilog_dialect {
|
||||
args.write_args(dialect.firtool_extra_args().iter().copied());
|
||||
}
|
||||
args.write_interned_args(firtool_extra_args);
|
||||
}
|
||||
|
||||
fn current_dir(job: &ExternalCommandJob<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,12 +3,14 @@
|
|||
|
||||
use crate::{
|
||||
expr::{
|
||||
CastToBits, Expr, ReduceBits, ToExpr,
|
||||
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
|
||||
CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType,
|
||||
Valueless,
|
||||
ops::{ArrayLiteral, BundleLiteral},
|
||||
value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue},
|
||||
},
|
||||
int::{Bool, DynSize},
|
||||
intern::{Intern, Interned},
|
||||
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
|
||||
intern::{Intern, InternSlice, Interned},
|
||||
sim::value::{SimValue, SimValueEq, ToSimValue, ToSimValueWithType},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize,
|
||||
|
|
@ -18,7 +20,7 @@ use crate::{
|
|||
util::HashMap,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, marker::PhantomData};
|
||||
use std::{borrow::Cow, fmt, marker::PhantomData};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BundleField {
|
||||
|
|
@ -271,7 +273,6 @@ impl Type for Bundle {
|
|||
|
||||
pub trait BundleType: Type<BaseType = Bundle> {
|
||||
type Builder: Default;
|
||||
type FilledBuilder: ToExpr<Type = Self>;
|
||||
fn fields(&self) -> Interned<[BundleField]>;
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +319,7 @@ impl<'a> BundleSimValueFromOpaque<'a> {
|
|||
#[track_caller]
|
||||
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>();
|
||||
assert_eq!(field_ty, SimValue::ty(field_value));
|
||||
assert_eq!(field_ty, field_value.ty());
|
||||
SimValue::opaque_mut(field_value).clone_from_slice(field_opaque);
|
||||
}
|
||||
}
|
||||
|
|
@ -354,7 +355,7 @@ impl<'a> BundleSimValueToOpaque<'a> {
|
|||
else {
|
||||
panic!("tried to write too many fields with BundleSimValueToOpaque");
|
||||
};
|
||||
assert_eq!(T::from_canonical(ty), SimValue::ty(field_value));
|
||||
assert_eq!(T::from_canonical(ty), field_value.ty());
|
||||
self.writer.fill_prefix_with(ty.size(), |writer| {
|
||||
writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice())
|
||||
});
|
||||
|
|
@ -374,17 +375,8 @@ impl<'a> BundleSimValueToOpaque<'a> {
|
|||
#[derive(Default)]
|
||||
pub struct NoBuilder;
|
||||
|
||||
pub struct Unfilled<T: Type>(PhantomData<T>);
|
||||
|
||||
impl<T: Type> Default for Unfilled<T> {
|
||||
fn default() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl BundleType for Bundle {
|
||||
type Builder = NoBuilder;
|
||||
type FilledBuilder = Expr<Bundle>;
|
||||
fn fields(&self) -> Interned<[BundleField]> {
|
||||
self.0.fields
|
||||
}
|
||||
|
|
@ -420,15 +412,14 @@ macro_rules! impl_tuple_builder_fields {
|
|||
) => {
|
||||
impl<
|
||||
$($head_type_var,)*
|
||||
$cur_type_var: Type,
|
||||
$($tail_type_var,)*
|
||||
> TupleBuilder<(
|
||||
$($head_type_var,)*
|
||||
Unfilled<$cur_type_var>,
|
||||
(),
|
||||
$($tail_type_var,)*
|
||||
)>
|
||||
{
|
||||
pub fn $cur_field(self, $cur_var: impl ToExpr<Type = $cur_type_var>) -> TupleBuilder<(
|
||||
pub fn $cur_field<$cur_type_var: Type>(self, $cur_var: impl ToExpr<Type = $cur_type_var>) -> TupleBuilder<(
|
||||
$($head_type_var,)*
|
||||
Expr<$cur_type_var>,
|
||||
$($tail_type_var,)*
|
||||
|
|
@ -452,6 +443,12 @@ macro_rules! impl_tuple_builder_fields {
|
|||
($global:tt []) => {};
|
||||
}
|
||||
|
||||
macro_rules! get_unit_ty {
|
||||
($($tt:tt)*) => {
|
||||
()
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_tuples {
|
||||
(
|
||||
[$({
|
||||
|
|
@ -545,11 +542,10 @@ macro_rules! impl_tuples {
|
|||
}
|
||||
}
|
||||
impl<$($T: Type,)*> BundleType for ($($T,)*) {
|
||||
type Builder = TupleBuilder<($(Unfilled<$T>,)*)>;
|
||||
type FilledBuilder = TupleBuilder<($(Expr<$T>,)*)>;
|
||||
type Builder = TupleBuilder<($(get_unit_ty!($T),)*)>;
|
||||
fn fields(&self) -> Interned<[BundleField]> {
|
||||
let ($($var,)*) = self;
|
||||
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern()
|
||||
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*].intern_slice()
|
||||
}
|
||||
}
|
||||
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
|
||||
|
|
@ -572,25 +568,56 @@ macro_rules! impl_tuples {
|
|||
builder.finish()
|
||||
};
|
||||
}
|
||||
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
|
||||
impl<'a, $($T: ToSimValue,)*> ToSimValueInner<'a> for ($($T,)*)
|
||||
where
|
||||
Self: ValueType<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 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> {
|
||||
let ($($var,)*) = self;
|
||||
$(let $var = $var.to_expr();)*
|
||||
let ty = ($(Expr::ty($var),)*);
|
||||
let ty = ($($var.ty(),)*);
|
||||
let field_values = [$(Expr::canonical($var)),*];
|
||||
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
|
||||
BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
|
||||
}
|
||||
}
|
||||
impl<$($T: Type,)*> ValueType for TupleBuilder<($(Expr<$T>,)*)> {
|
||||
type Type = ($($T,)*);
|
||||
type ValueCategory = ValueCategoryExpr;
|
||||
fn ty(&self) -> Self::Type {
|
||||
let ($($var,)*) = self.0;
|
||||
($($var.ty(),)*)
|
||||
}
|
||||
}
|
||||
impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
|
||||
type Type = ($($T,)*);
|
||||
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
let ($($var,)*) = self.0;
|
||||
let ty = ($(Expr::ty($var),)*);
|
||||
let ty = ($($var.ty(),)*);
|
||||
let field_values = [$(Expr::canonical($var)),*];
|
||||
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
|
||||
BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
|
||||
}
|
||||
}
|
||||
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) {
|
||||
|
|
@ -624,7 +651,7 @@ macro_rules! impl_tuples {
|
|||
};
|
||||
let mut opaque = OpaqueSimValue::empty();
|
||||
$(let $var = $var.into_sim_value_with_type($ty_var.ty);
|
||||
assert_eq!(SimValue::ty(&$var), $ty_var.ty);
|
||||
assert_eq!($var.ty(), $ty_var.ty);
|
||||
opaque.extend_from_slice(SimValue::opaque(&$var).as_slice());
|
||||
)*
|
||||
SimValue::from_opaque(ty, opaque)
|
||||
|
|
@ -646,53 +673,82 @@ macro_rules! impl_tuples {
|
|||
SimValue::from_value(ty, ($($var,)*))
|
||||
}
|
||||
}
|
||||
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) {
|
||||
type Type = ($($T::Type,)*);
|
||||
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*)
|
||||
where
|
||||
Self: ValueType<Type = ($($T::Type,)*)>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
let ($($var,)*) = self;
|
||||
$(let $var = $var.to_sim_value();)*
|
||||
SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
|
||||
SimValue::from_value(($($var.ty(),)*), ($($var,)*))
|
||||
}
|
||||
#[track_caller]
|
||||
fn into_sim_value(self) -> SimValue<Self::Type> {
|
||||
let ($($var,)*) = self;
|
||||
$(let $var = $var.to_sim_value();)*
|
||||
SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
|
||||
SimValue::from_value(($($var.ty(),)*), ($($var,)*))
|
||||
}
|
||||
}
|
||||
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
impl<$($Lhs: Type + HdlPartialEqImpl<$Rhs>, $Rhs: Type,)*> HdlPartialEqImpl<($($Rhs,)*)> for ($($Lhs,)*) {
|
||||
#[track_caller]
|
||||
fn cmp_value_eq(
|
||||
lhs: Self,
|
||||
lhs_value: Cow<'_, Self::SimValue>,
|
||||
rhs: ($($Rhs,)*),
|
||||
rhs_value: Cow<'_, <($($Rhs,)*) as Type>::SimValue>,
|
||||
) -> bool {
|
||||
#![allow(unused_variables)]
|
||||
let ($($lhs_var,)*) = &*lhs_value;
|
||||
let ($($rhs_var,)*) = &*rhs_value;
|
||||
let retval = true;
|
||||
$(let retval = retval && $Lhs::cmp_value_eq(lhs.$num, Cow::Borrowed($lhs_var), rhs.$num, Cow::Borrowed($rhs_var));)*
|
||||
retval
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
let ($($lhs_var,)*) = *lhs;
|
||||
let ($($rhs_var,)*) = *rhs;
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
Bool,
|
||||
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
|
||||
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_eq($lhs_var, $rhs_var)),)*]),
|
||||
)
|
||||
.cast_to_bits()
|
||||
.all_one_bits()
|
||||
}
|
||||
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
#[track_caller]
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
let ($($lhs_var,)*) = *lhs;
|
||||
let ($($rhs_var,)*) = *rhs;
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
Bool,
|
||||
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
|
||||
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_ne($lhs_var, $rhs_var)),)*]),
|
||||
)
|
||||
.cast_to_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,)*) {
|
||||
fn sim_value_eq(lhs: &SimValue<Self>, rhs: &SimValue<($($Rhs,)*)>) -> bool {
|
||||
let ($($lhs_var,)*) = &**lhs;
|
||||
let ($($rhs_var,)*) = &**rhs;
|
||||
let retval = true;
|
||||
$(let retval = retval && $lhs_var == $rhs_var;)*
|
||||
retval
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_valueless_ne(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_ne(Valueless::new($lhs_var), Valueless::new($rhs_var));)*
|
||||
Valueless::new(Bool)
|
||||
}
|
||||
}
|
||||
impl<$($T: SimValueEq + HdlPartialEqImpl<$T>,)*> SimValueEq for ($($T,)*) {}
|
||||
};
|
||||
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
||||
impl_tuples!([$($lhs)*] []);
|
||||
|
|
@ -781,9 +837,16 @@ impl<T: ?Sized + Send + Sync + 'static> Default for PhantomDataBuilder<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> ToExpr 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> {
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
PhantomData.to_expr()
|
||||
}
|
||||
|
|
@ -791,7 +854,6 @@ impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
|
|||
|
||||
impl<T: ?Sized + Send + Sync + 'static> BundleType for PhantomData<T> {
|
||||
type Builder = PhantomDataBuilder<T>;
|
||||
type FilledBuilder = PhantomDataBuilder<T>;
|
||||
fn fields(&self) -> Interned<[BundleField]> {
|
||||
Interned::default()
|
||||
}
|
||||
|
|
@ -810,17 +872,22 @@ impl<T: ?Sized + Send + Sync + 'static> StaticType for PhantomData<T> {
|
|||
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
|
||||
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> {
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> {
|
||||
type Type = PhantomData<T>;
|
||||
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self) -> SimValue<Self> {
|
||||
SimValue::from_value(*self, *self)
|
||||
|
|
|
|||
|
|
@ -1,806 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
bundle::{Bundle, BundleType},
|
||||
firrtl::{self, ExportOptions},
|
||||
intern::Interned,
|
||||
module::Module,
|
||||
util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8},
|
||||
};
|
||||
use clap::{
|
||||
Parser, Subcommand, ValueEnum, ValueHint,
|
||||
builder::{OsStringValueParser, TypedValueParser},
|
||||
};
|
||||
use eyre::{Report, eyre};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
error,
|
||||
ffi::OsString,
|
||||
fmt::{self, Write},
|
||||
fs, io, mem,
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
};
|
||||
use tempfile::TempDir;
|
||||
|
||||
pub type Result<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,10 +1,11 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::{Expr, ToExpr},
|
||||
expr::{Expr, ValueType},
|
||||
hdl,
|
||||
int::Bool,
|
||||
reset::{Reset, ResetType},
|
||||
sim::value::SimValue,
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
||||
|
|
@ -91,29 +92,34 @@ impl StaticType for Clock {
|
|||
}
|
||||
|
||||
pub trait ToClock {
|
||||
fn to_clock(&self) -> Expr<Clock>;
|
||||
type Output: ValueType<Type = Clock>;
|
||||
fn to_clock(&self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + ToClock> ToClock for &'_ T {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
type Output = T::Output;
|
||||
fn to_clock(&self) -> Self::Output {
|
||||
(**self).to_clock()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + ToClock> ToClock for &'_ mut T {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
type Output = T::Output;
|
||||
fn to_clock(&self) -> Self::Output {
|
||||
(**self).to_clock()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + ToClock> ToClock for Box<T> {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
type Output = T::Output;
|
||||
fn to_clock(&self) -> Self::Output {
|
||||
(**self).to_clock()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToClock for Expr<Clock> {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
type Output = Expr<Clock>;
|
||||
fn to_clock(&self) -> Self::Output {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
|
@ -125,7 +131,25 @@ pub struct ClockDomain<R: ResetType = Reset> {
|
|||
}
|
||||
|
||||
impl ToClock for bool {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
self.to_expr().to_clock()
|
||||
type Output = SimValue<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,10 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{
|
||||
Expr, ToExpr,
|
||||
ops::{ExprPartialEq, VariantAccess},
|
||||
},
|
||||
expr::{Expr, HdlPartialEq, HdlPartialEqImpl, ToExpr, ValueType, ops::VariantAccess},
|
||||
hdl,
|
||||
int::{Bool, UIntValue},
|
||||
intern::{Intern, Interned},
|
||||
|
|
@ -13,7 +10,7 @@ use crate::{
|
|||
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect,
|
||||
enum_match_variants_helper, incomplete_wire, wire,
|
||||
},
|
||||
sim::value::{SimValue, SimValuePartialEq},
|
||||
sim::value::SimValue,
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize,
|
||||
|
|
@ -24,7 +21,7 @@ use crate::{
|
|||
};
|
||||
use bitvec::{order::Lsb0, slice::BitSlice, view::BitView};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc};
|
||||
use std::{borrow::Cow, convert::Infallible, fmt, iter::FusedIterator, sync::Arc};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub struct EnumVariant {
|
||||
|
|
@ -599,7 +596,7 @@ impl<'a> EnumSimValueFromOpaque<'a> {
|
|||
let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else {
|
||||
self.usage_error(true);
|
||||
};
|
||||
assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
|
||||
assert_eq!(value.ty(), T::from_canonical(variant_ty));
|
||||
SimValue::bits_mut(value)
|
||||
.bits_mut()
|
||||
.copy_from_bitslice(variant_bits);
|
||||
|
|
@ -711,7 +708,7 @@ impl<'a> EnumSimValueToOpaque<'a> {
|
|||
let Some(variant_ty) = self.variants[discriminant].ty else {
|
||||
panic!("expected variant to have no field");
|
||||
};
|
||||
assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
|
||||
assert_eq!(value.ty(), T::from_canonical(variant_ty));
|
||||
self.known_variant(discriminant, Some(SimValue::opaque(value)), padding)
|
||||
}
|
||||
}
|
||||
|
|
@ -732,9 +729,38 @@ pub enum HdlOption<T: Type> {
|
|||
HdlSome(T),
|
||||
}
|
||||
|
||||
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
|
||||
impl<Lhs: Type + HdlPartialEqImpl<Rhs>, Rhs: Type> HdlPartialEqImpl<HdlOption<Rhs>>
|
||||
for HdlOption<Lhs>
|
||||
{
|
||||
fn cmp_value_eq(
|
||||
lhs: Self,
|
||||
lhs_value: Cow<'_, Self::SimValue>,
|
||||
rhs: HdlOption<Rhs>,
|
||||
rhs_value: Cow<'_, <HdlOption<Rhs> as Type>::SimValue>,
|
||||
) -> bool {
|
||||
type SimValueMatch<T> = <T as Type>::SimValue;
|
||||
match (&*lhs_value, &*rhs_value) {
|
||||
(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, _),
|
||||
) => HdlPartialEqImpl::cmp_value_eq(
|
||||
lhs.HdlSome,
|
||||
Cow::Borrowed(&**l),
|
||||
rhs.HdlSome,
|
||||
Cow::Borrowed(&**r),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let cmp_eq = wire();
|
||||
#[hdl]
|
||||
|
|
@ -743,7 +769,7 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
|
|||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
|
||||
HdlSome(rhs) => connect(cmp_eq, lhs.cmp_eq(rhs)),
|
||||
HdlNone => connect(cmp_eq, false),
|
||||
}
|
||||
}
|
||||
|
|
@ -760,7 +786,7 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
|
|||
}
|
||||
|
||||
#[hdl]
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let cmp_ne = wire();
|
||||
#[hdl]
|
||||
|
|
@ -769,7 +795,7 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
|
|||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
|
||||
HdlSome(rhs) => connect(cmp_ne, lhs.cmp_ne(rhs)),
|
||||
HdlNone => connect(cmp_ne, true),
|
||||
}
|
||||
}
|
||||
|
|
@ -786,25 +812,6 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
|
|||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
||||
HdlOption[T::TYPE].HdlNone()
|
||||
|
|
@ -813,7 +820,7 @@ pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
|||
#[allow(non_snake_case)]
|
||||
pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
|
||||
let value = value.to_expr();
|
||||
HdlOption[Expr::ty(value)].HdlSome(value)
|
||||
HdlOption[value.ty()].HdlSome(value)
|
||||
}
|
||||
|
||||
impl<T: Type> HdlOption<T> {
|
||||
|
|
@ -853,7 +860,7 @@ impl<T: Type> HdlOption<T> {
|
|||
let value = f(value).inspect_err(|_| {
|
||||
and_then_out.complete(()); // avoid error
|
||||
})?;
|
||||
let and_then_out = and_then_out.complete(Expr::ty(value));
|
||||
let and_then_out = and_then_out.complete(value.ty());
|
||||
connect(and_then_out, value);
|
||||
drop(some_scope);
|
||||
let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) =
|
||||
|
|
@ -861,7 +868,7 @@ impl<T: Type> HdlOption<T> {
|
|||
else {
|
||||
unreachable!();
|
||||
};
|
||||
connect(and_then_out, Expr::ty(and_then_out).HdlNone());
|
||||
connect(and_then_out, and_then_out.ty().HdlNone());
|
||||
drop(none_scope);
|
||||
Ok(and_then_out)
|
||||
}
|
||||
|
|
@ -879,8 +886,8 @@ impl<T: Type> HdlOption<T> {
|
|||
#[track_caller]
|
||||
pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> {
|
||||
#[hdl]
|
||||
let and_out = wire(Expr::ty(opt_b));
|
||||
connect(and_out, Expr::ty(opt_b).HdlNone());
|
||||
let and_out = wire(opt_b.ty());
|
||||
connect(and_out, opt_b.ty().HdlNone());
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
connect(and_out, opt_b);
|
||||
|
|
@ -894,8 +901,8 @@ impl<T: Type> HdlOption<T> {
|
|||
f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>,
|
||||
) -> Result<Expr<Self>, E> {
|
||||
#[hdl]
|
||||
let filtered = wire(Expr::ty(expr));
|
||||
connect(filtered, Expr::ty(expr).HdlNone());
|
||||
let filtered = wire(expr.ty());
|
||||
connect(filtered, expr.ty().HdlNone());
|
||||
let mut f = Some(f);
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
|
|
@ -969,7 +976,7 @@ impl<T: Type> HdlOption<T> {
|
|||
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
||||
) -> Expr<R> {
|
||||
#[hdl]
|
||||
let mapped = wire(Expr::ty(default));
|
||||
let mapped = wire(default.ty());
|
||||
let mut f = Some(f);
|
||||
#[hdl]
|
||||
match expr {
|
||||
|
|
@ -994,12 +1001,12 @@ impl<T: Type> HdlOption<T> {
|
|||
match expr {
|
||||
HdlSome(v) => {
|
||||
let v = f.take().unwrap()(v);
|
||||
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
|
||||
let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty()));
|
||||
connect(mapped, v);
|
||||
}
|
||||
HdlNone => {
|
||||
let v = default.take().unwrap()();
|
||||
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
|
||||
let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty()));
|
||||
connect(mapped, v);
|
||||
}
|
||||
}
|
||||
|
|
@ -1009,7 +1016,7 @@ impl<T: Type> HdlOption<T> {
|
|||
#[track_caller]
|
||||
pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
|
||||
#[hdl]
|
||||
let or_out = wire(Expr::ty(expr));
|
||||
let or_out = wire(expr.ty());
|
||||
connect(or_out, opt_b);
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
|
|
@ -1021,7 +1028,7 @@ impl<T: Type> HdlOption<T> {
|
|||
#[track_caller]
|
||||
pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> {
|
||||
#[hdl]
|
||||
let or_else_out = wire(Expr::ty(expr));
|
||||
let or_else_out = wire(expr.ty());
|
||||
connect(or_else_out, f());
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
|
|
@ -1033,7 +1040,7 @@ impl<T: Type> HdlOption<T> {
|
|||
#[track_caller]
|
||||
pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> {
|
||||
#[hdl]
|
||||
let unwrap_or_else_out = wire(Expr::ty(default));
|
||||
let unwrap_or_else_out = wire(default.ty());
|
||||
connect(unwrap_or_else_out, default);
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
|
|
@ -1045,7 +1052,7 @@ impl<T: Type> HdlOption<T> {
|
|||
#[track_caller]
|
||||
pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> {
|
||||
#[hdl]
|
||||
let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome);
|
||||
let unwrap_or_else_out = wire(expr.ty().HdlSome);
|
||||
connect(unwrap_or_else_out, f());
|
||||
#[hdl]
|
||||
if let HdlSome(v) = expr {
|
||||
|
|
@ -1057,14 +1064,14 @@ impl<T: Type> HdlOption<T> {
|
|||
#[track_caller]
|
||||
pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
|
||||
#[hdl]
|
||||
let xor_out = wire(Expr::ty(expr));
|
||||
let xor_out = wire(expr.ty());
|
||||
#[hdl]
|
||||
if let HdlSome(_) = expr {
|
||||
#[hdl]
|
||||
if let HdlNone = opt_b {
|
||||
connect(xor_out, expr);
|
||||
} else {
|
||||
connect(xor_out, Expr::ty(expr).HdlNone());
|
||||
connect(xor_out, expr.ty().HdlNone());
|
||||
}
|
||||
} else {
|
||||
connect(xor_out, opt_b);
|
||||
|
|
@ -1075,8 +1082,8 @@ impl<T: Type> HdlOption<T> {
|
|||
#[track_caller]
|
||||
pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> {
|
||||
#[hdl]
|
||||
let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]);
|
||||
connect(zip_out, Expr::ty(zip_out).HdlNone());
|
||||
let zip_out = wire(HdlOption[(expr.ty().HdlSome, other.ty().HdlSome)]);
|
||||
connect(zip_out, zip_out.ty().HdlNone());
|
||||
#[hdl]
|
||||
if let HdlSome(l) = expr {
|
||||
#[hdl]
|
||||
|
|
@ -1093,11 +1100,11 @@ impl<T: Type> HdlOption<HdlOption<T>> {
|
|||
#[track_caller]
|
||||
pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> {
|
||||
#[hdl]
|
||||
let flattened = wire(Expr::ty(expr).HdlSome);
|
||||
let flattened = wire(expr.ty().HdlSome);
|
||||
#[hdl]
|
||||
match expr {
|
||||
HdlSome(v) => connect(flattened, v),
|
||||
HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()),
|
||||
HdlNone => connect(flattened, expr.ty().HdlSome.HdlNone()),
|
||||
}
|
||||
flattened
|
||||
}
|
||||
|
|
@ -1107,7 +1114,7 @@ impl<T: Type, U: Type> HdlOption<(T, U)> {
|
|||
#[hdl]
|
||||
#[track_caller]
|
||||
pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> {
|
||||
let (t, u) = Expr::ty(expr).HdlSome;
|
||||
let (t, u) = expr.ty().HdlSome;
|
||||
#[hdl]
|
||||
let unzipped = wire((HdlOption[t], HdlOption[u]));
|
||||
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
222
crates/fayalite/src/expr/ops/test_ops_impls.rs
Normal file
222
crates/fayalite/src/expr/ops/test_ops_impls.rs
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{expr::Valueless, expr::ops::make_impls, prelude::*};
|
||||
use std::num::NonZero;
|
||||
|
||||
macro_rules! assert_neg_impls {
|
||||
([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => {
|
||||
const _: () = {
|
||||
fn _check_neg_impl<$($Lifetimes)*$($Bounds)*>(v: $ty)
|
||||
-> impl ValueType<
|
||||
ValueCategory = <$ty as ValueType>::ValueCategory,
|
||||
Type = <<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,7 +3,7 @@
|
|||
use crate::{
|
||||
array::Array,
|
||||
bundle::{Bundle, BundleField},
|
||||
expr::{Expr, Flow, ToExpr},
|
||||
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr},
|
||||
intern::{Intern, Interned},
|
||||
memory::{DynPortType, MemPort},
|
||||
module::{Instance, ModuleIO, TargetName},
|
||||
|
|
@ -196,9 +196,18 @@ macro_rules! impl_target_base {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToExpr for $TargetBase {
|
||||
impl ValueType for $TargetBase {
|
||||
type Type = CanonicalType;
|
||||
type ValueCategory = ValueCategoryExpr;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.ty().canonical(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToExpr for $TargetBase {
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
match self {
|
||||
$(Self::$Variant(v) => Expr::canonical(v.to_expr()),)*
|
||||
|
|
|
|||
378
crates/fayalite/src/expr/value_category.rs
Normal file
378
crates/fayalite/src/expr/value_category.rs
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{expr::ValueType, ty::Type};
|
||||
|
||||
trait ValueCategorySealed {}
|
||||
|
||||
#[expect(private_bounds)]
|
||||
pub trait ValueCategory:
|
||||
ValueCategorySealed
|
||||
+ Copy
|
||||
+ Ord
|
||||
+ Default
|
||||
+ std::fmt::Debug
|
||||
+ std::hash::Hash
|
||||
+ 'static
|
||||
+ Send
|
||||
+ Sync
|
||||
{
|
||||
type DispatchOutput<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,
|
||||
);
|
||||
}
|
||||
|
|
@ -4,14 +4,15 @@
|
|||
use crate::{
|
||||
annotations::{
|
||||
Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
|
||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
|
||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation,
|
||||
},
|
||||
array::Array,
|
||||
build::{ToArgs, WriteArgs},
|
||||
bundle::{Bundle, BundleField, BundleType},
|
||||
clock::Clock,
|
||||
enum_::{Enum, EnumType, EnumVariant},
|
||||
expr::{
|
||||
CastBitsTo, Expr, ExprEnum,
|
||||
CastBitsTo, Expr, ExprEnum, ToExpr, ValueType,
|
||||
ops::{self, VariantAccess},
|
||||
target::{
|
||||
Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
|
||||
|
|
@ -23,9 +24,9 @@ use crate::{
|
|||
memory::{Mem, PortKind, PortName, ReadUnderWrite},
|
||||
module::{
|
||||
AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter,
|
||||
ExternModuleParameterValue, Module, ModuleBody, NameOptId, NormalModuleBody, Stmt,
|
||||
StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg,
|
||||
StmtWire,
|
||||
ExternModuleParameterValue, Module, ModuleBody, ModuleIO, NameId, NameOptId,
|
||||
NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance,
|
||||
StmtMatch, StmtReg, StmtWire,
|
||||
transform::{
|
||||
simplify_enums::{SimplifyEnumsError, SimplifyEnumsKind, simplify_enums},
|
||||
simplify_memories::simplify_memories,
|
||||
|
|
@ -38,21 +39,23 @@ use crate::{
|
|||
BitSliceWriteWithBase, DebugAsRawString, GenericConstBool, HashMap, HashSet,
|
||||
const_str_array_is_strictly_ascending,
|
||||
},
|
||||
vendor::xilinx::XilinxAnnotation,
|
||||
};
|
||||
use bitvec::slice::BitSlice;
|
||||
use clap::value_parser;
|
||||
use num_traits::Signed;
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, VecDeque},
|
||||
error::Error,
|
||||
ffi::OsString,
|
||||
fmt::{self, Write},
|
||||
fs,
|
||||
hash::Hash,
|
||||
io,
|
||||
ops::Range,
|
||||
ops::{ControlFlow, Range},
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
|
|
@ -404,10 +407,10 @@ impl TypeState {
|
|||
self.next_type_name.set(id + 1);
|
||||
Ident(Intern::intern_owned(format!("Ty{id}")))
|
||||
}
|
||||
fn get_bundle_field(&mut self, ty: Bundle, name: Interned<str>) -> Result<Ident> {
|
||||
fn get_bundle_field(&mut self, ty: Bundle, name: Interned<str>) -> Result<Ident, FirrtlError> {
|
||||
Ok(self.bundle_ns(ty)?.borrow_mut().get(name))
|
||||
}
|
||||
fn bundle_def(&self, ty: Bundle) -> Result<(Ident, Rc<RefCell<Namespace>>)> {
|
||||
fn bundle_def(&self, ty: Bundle) -> Result<(Ident, Rc<RefCell<Namespace>>), FirrtlError> {
|
||||
self.bundle_defs.get_or_make(ty, |&ty, definitions| {
|
||||
let mut ns = Namespace::default();
|
||||
let mut body = String::new();
|
||||
|
|
@ -428,13 +431,13 @@ impl TypeState {
|
|||
Ok((name, Rc::new(RefCell::new(ns))))
|
||||
})
|
||||
}
|
||||
fn bundle_ty(&self, ty: Bundle) -> Result<Ident> {
|
||||
fn bundle_ty(&self, ty: Bundle) -> Result<Ident, FirrtlError> {
|
||||
Ok(self.bundle_def(ty)?.0)
|
||||
}
|
||||
fn bundle_ns(&self, ty: Bundle) -> Result<Rc<RefCell<Namespace>>> {
|
||||
fn bundle_ns(&self, ty: Bundle) -> Result<Rc<RefCell<Namespace>>, FirrtlError> {
|
||||
Ok(self.bundle_def(ty)?.1)
|
||||
}
|
||||
fn enum_def(&self, ty: Enum) -> Result<(Ident, Rc<EnumDef>)> {
|
||||
fn enum_def(&self, ty: Enum) -> Result<(Ident, Rc<EnumDef>), FirrtlError> {
|
||||
self.enum_defs.get_or_make(ty, |&ty, definitions| {
|
||||
let mut variants = Namespace::default();
|
||||
let mut body = String::new();
|
||||
|
|
@ -461,13 +464,13 @@ impl TypeState {
|
|||
))
|
||||
})
|
||||
}
|
||||
fn enum_ty(&self, ty: Enum) -> Result<Ident> {
|
||||
fn enum_ty(&self, ty: Enum) -> Result<Ident, FirrtlError> {
|
||||
Ok(self.enum_def(ty)?.0)
|
||||
}
|
||||
fn get_enum_variant(&mut self, ty: Enum, name: Interned<str>) -> Result<Ident> {
|
||||
fn get_enum_variant(&mut self, ty: Enum, name: Interned<str>) -> Result<Ident, FirrtlError> {
|
||||
Ok(self.enum_def(ty)?.1.variants.borrow_mut().get(name))
|
||||
}
|
||||
fn ty<T: Type>(&self, ty: T) -> Result<String> {
|
||||
fn ty<T: Type>(&self, ty: T) -> Result<String, FirrtlError> {
|
||||
Ok(match ty.canonical() {
|
||||
CanonicalType::Bundle(ty) => self.bundle_ty(ty)?.to_string(),
|
||||
CanonicalType::Enum(ty) => self.enum_ty(ty)?.to_string(),
|
||||
|
|
@ -485,7 +488,7 @@ impl TypeState {
|
|||
CanonicalType::Reset(Reset {}) => "Reset".into(),
|
||||
CanonicalType::PhantomConst(_) => "{}".into(),
|
||||
CanonicalType::DynSimOnly(_) => {
|
||||
return Err(FirrtlError::SimOnlyValuesAreNotPermitted.into());
|
||||
return Err(FirrtlError::SimOnlyValuesAreNotPermitted);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -874,7 +877,7 @@ impl<'a> Exporter<'a> {
|
|||
definitions: &RcDefinitions,
|
||||
const_ty: bool,
|
||||
) -> Result<String> {
|
||||
let from_ty = Expr::ty(value);
|
||||
let from_ty = value.ty();
|
||||
let mut value = self.expr(Expr::canonical(value), definitions, const_ty)?;
|
||||
if from_ty.width().checked_add(1) == Some(to_ty.width())
|
||||
&& !FromTy::Signed::VALUE
|
||||
|
|
@ -923,7 +926,7 @@ impl<'a> Exporter<'a> {
|
|||
definitions: &RcDefinitions,
|
||||
const_ty: bool,
|
||||
) -> Result<String> {
|
||||
let base_width = Expr::ty(base).width();
|
||||
let base_width = base.ty().width();
|
||||
let base = self.expr(Expr::canonical(base), definitions, const_ty)?;
|
||||
if range.is_empty() {
|
||||
Ok(format!("tail({base}, {base_width})"))
|
||||
|
|
@ -1206,9 +1209,7 @@ impl<'a> Exporter<'a> {
|
|||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::Reset(_) => Ok(format!("asUInt({value_str})")),
|
||||
CanonicalType::PhantomConst(_) => Ok("UInt<0>(0)".into()),
|
||||
CanonicalType::DynSimOnly(_) => {
|
||||
Err(FirrtlError::SimOnlyValuesAreNotPermitted.into())
|
||||
}
|
||||
CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()),
|
||||
}
|
||||
}
|
||||
fn expr_cast_bits_to_bundle(
|
||||
|
|
@ -1429,9 +1430,7 @@ impl<'a> Exporter<'a> {
|
|||
definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}"));
|
||||
return Ok(retval.to_string());
|
||||
}
|
||||
CanonicalType::DynSimOnly(_) => {
|
||||
Err(FirrtlError::SimOnlyValuesAreNotPermitted.into())
|
||||
}
|
||||
CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()),
|
||||
}
|
||||
}
|
||||
fn expr_unary<T: Type>(
|
||||
|
|
@ -1471,7 +1470,7 @@ impl<'a> Exporter<'a> {
|
|||
ExprEnum::SIntLiteral(literal) => Ok(self.sint_literal(&literal)),
|
||||
ExprEnum::BoolLiteral(literal) => Ok(self.bool_literal(literal)),
|
||||
ExprEnum::PhantomConst(ty) => self.expr(
|
||||
UInt[0].zero().cast_bits_to(ty.canonical()),
|
||||
UInt[0].zero().to_expr().cast_bits_to(ty.canonical()),
|
||||
definitions,
|
||||
const_ty,
|
||||
),
|
||||
|
|
@ -1654,7 +1653,7 @@ impl<'a> Exporter<'a> {
|
|||
let value_str = self.expr(expr.arg(), definitions, const_ty)?;
|
||||
self.expr_cast_to_bits(
|
||||
value_str,
|
||||
Expr::ty(expr.arg()),
|
||||
expr.arg().ty(),
|
||||
definitions,
|
||||
Indent {
|
||||
indent_depth: &Cell::new(0),
|
||||
|
|
@ -1777,7 +1776,7 @@ impl<'a> Exporter<'a> {
|
|||
let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?;
|
||||
let name = self
|
||||
.type_state
|
||||
.get_bundle_field(Expr::ty(expr.base()), expr.field_name())?;
|
||||
.get_bundle_field(expr.base().ty(), expr.field_name())?;
|
||||
write!(out, ".{name}").unwrap();
|
||||
Ok(out)
|
||||
}
|
||||
|
|
@ -1884,7 +1883,11 @@ impl<'a> Exporter<'a> {
|
|||
}
|
||||
fn annotation(&mut self, path: AnnotationTargetPath, annotation: &Annotation) {
|
||||
let data = match annotation {
|
||||
Annotation::DontTouch(DontTouchAnnotation {}) => AnnotationData::DontTouch,
|
||||
Annotation::DontTouch(DontTouchAnnotation {}) => {
|
||||
// TODO: error if the annotated thing was renamed because of a naming conflict,
|
||||
// unless Target::base() is one of the ports of the top-level module since that's handled by ScalarizedModuleABI
|
||||
AnnotationData::DontTouch
|
||||
}
|
||||
Annotation::SVAttribute(SVAttributeAnnotation { text }) => {
|
||||
AnnotationData::AttributeAnnotation { description: *text }
|
||||
}
|
||||
|
|
@ -1907,6 +1910,9 @@ impl<'a> Exporter<'a> {
|
|||
class: str::to_string(class),
|
||||
additional_fields: (*additional_fields).into(),
|
||||
},
|
||||
Annotation::Xilinx(XilinxAnnotation::XdcLocation(_))
|
||||
| Annotation::Xilinx(XilinxAnnotation::XdcIOStandard(_))
|
||||
| Annotation::Xilinx(XilinxAnnotation::XdcCreateClock(_)) => return,
|
||||
};
|
||||
self.annotations.push(FirrtlAnnotation {
|
||||
data,
|
||||
|
|
@ -2095,12 +2101,12 @@ impl<'a> Exporter<'a> {
|
|||
rhs,
|
||||
source_location,
|
||||
}) => {
|
||||
if Expr::ty(lhs) != Expr::ty(rhs) {
|
||||
if lhs.ty() != rhs.ty() {
|
||||
writeln!(
|
||||
body,
|
||||
"{indent}; connect different types:\n{indent}; lhs: {:?}\n{indent}; rhs: {:?}",
|
||||
Expr::ty(lhs),
|
||||
Expr::ty(rhs),
|
||||
lhs.ty(),
|
||||
rhs.ty(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
|
@ -2194,7 +2200,7 @@ impl<'a> Exporter<'a> {
|
|||
FileInfo::new(source_location),
|
||||
)
|
||||
.unwrap();
|
||||
let enum_ty = Expr::ty(expr);
|
||||
let enum_ty = expr.ty();
|
||||
let match_arms_indent = indent.push();
|
||||
for ((variant_index, variant), match_arm_block) in
|
||||
enum_ty.variants().iter().enumerate().zip(blocks)
|
||||
|
|
@ -2320,6 +2326,7 @@ impl<'a> Exporter<'a> {
|
|||
ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name,
|
||||
parameters,
|
||||
clocks_for_past: _,
|
||||
simulation: _,
|
||||
}) => {
|
||||
let verilog_name = Ident(verilog_name);
|
||||
|
|
@ -2454,7 +2461,7 @@ impl<T: ?Sized + FileBackendTrait> FileBackendTrait for &'_ mut T {
|
|||
pub struct FileBackend {
|
||||
pub dir_path: PathBuf,
|
||||
pub circuit_name: Option<String>,
|
||||
pub top_fir_file_stem: Option<String>,
|
||||
pub top_fir_file_stem: Option<OsString>,
|
||||
}
|
||||
|
||||
impl FileBackend {
|
||||
|
|
@ -2503,7 +2510,7 @@ impl FileBackendTrait for FileBackend {
|
|||
) -> Result<(), Self::Error> {
|
||||
let top_fir_file_stem = self
|
||||
.top_fir_file_stem
|
||||
.get_or_insert_with(|| circuit_name.clone());
|
||||
.get_or_insert_with(|| circuit_name.clone().into());
|
||||
self.circuit_name = Some(circuit_name);
|
||||
let mut path = self.dir_path.join(top_fir_file_stem);
|
||||
if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) {
|
||||
|
|
@ -2677,21 +2684,12 @@ impl FileBackendTrait for TestBackend {
|
|||
|
||||
fn export_impl(
|
||||
file_backend: &mut dyn WrappedFileBackendTrait,
|
||||
mut top_module: Interned<Module<Bundle>>,
|
||||
top_module: Interned<Module<Bundle>>,
|
||||
options: ExportOptions,
|
||||
) -> Result<(), WrappedError> {
|
||||
let ExportOptions {
|
||||
simplify_memories: do_simplify_memories,
|
||||
simplify_enums: do_simplify_enums,
|
||||
__private: _,
|
||||
} = options;
|
||||
if let Some(kind) = do_simplify_enums {
|
||||
top_module =
|
||||
simplify_enums(top_module, kind).map_err(|e| file_backend.simplify_enums_error(e))?;
|
||||
}
|
||||
if do_simplify_memories {
|
||||
top_module = simplify_memories(top_module);
|
||||
}
|
||||
let top_module = options
|
||||
.prepare_top_module(top_module)
|
||||
.map_err(|e| file_backend.simplify_enums_error(e))?;
|
||||
let indent_depth = Cell::new(0);
|
||||
let mut global_ns = Namespace::default();
|
||||
let circuit_name = global_ns.get(top_module.name_id());
|
||||
|
|
@ -2753,14 +2751,23 @@ impl clap::builder::TypedValueParser for OptionSimplifyEnumsKindValueParser {
|
|||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ExportOptionsPrivate(());
|
||||
|
||||
#[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
impl ExportOptionsPrivate {
|
||||
fn private_new() -> Self {
|
||||
Self(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct ExportOptions {
|
||||
#[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)]
|
||||
#[serde(default = "ExportOptions::default_simplify_memories")]
|
||||
pub simplify_memories: bool,
|
||||
#[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = "replace-with-bundle-of-uints")]
|
||||
pub simplify_enums: std::option::Option<SimplifyEnumsKind>,
|
||||
#[serde(default = "ExportOptions::default_simplify_enums")]
|
||||
pub simplify_enums: std::option::Option<SimplifyEnumsKind>, // use std::option::Option instead of Option to avoid clap mis-parsing
|
||||
#[doc(hidden)]
|
||||
#[clap(skip = ExportOptionsPrivate(()))]
|
||||
#[serde(skip, default = "ExportOptionsPrivate::private_new")]
|
||||
/// `#[non_exhaustive]` except allowing struct update syntax
|
||||
pub __private: ExportOptionsPrivate,
|
||||
}
|
||||
|
|
@ -2771,7 +2778,34 @@ impl fmt::Debug for ExportOptions {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToArgs for ExportOptions {
|
||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self {
|
||||
simplify_memories,
|
||||
simplify_enums,
|
||||
__private: ExportOptionsPrivate(()),
|
||||
} = *self;
|
||||
if !simplify_memories {
|
||||
args.write_arg("--no-simplify-memories");
|
||||
}
|
||||
let simplify_enums = simplify_enums.map(|v| {
|
||||
clap::ValueEnum::to_possible_value(&v).expect("there are no skipped variants")
|
||||
});
|
||||
let simplify_enums = match &simplify_enums {
|
||||
None => OptionSimplifyEnumsKindValueParser::NONE_NAME,
|
||||
Some(v) => v.get_name(),
|
||||
};
|
||||
args.write_long_option_eq("simplify-enums", simplify_enums);
|
||||
}
|
||||
}
|
||||
|
||||
impl ExportOptions {
|
||||
fn default_simplify_memories() -> bool {
|
||||
true
|
||||
}
|
||||
fn default_simplify_enums() -> Option<SimplifyEnumsKind> {
|
||||
Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts)
|
||||
}
|
||||
fn debug_fmt(
|
||||
&self,
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
|
|
@ -2823,18 +2857,47 @@ impl ExportOptions {
|
|||
if f.alternate() { "\n}" } else { " }" }
|
||||
)
|
||||
}
|
||||
fn prepare_top_module_helper(
|
||||
self,
|
||||
mut top_module: Interned<Module<Bundle>>,
|
||||
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
|
||||
let Self {
|
||||
simplify_memories: do_simplify_memories,
|
||||
simplify_enums: do_simplify_enums,
|
||||
__private: _,
|
||||
} = self;
|
||||
if let Some(kind) = do_simplify_enums {
|
||||
top_module = simplify_enums(top_module, kind)?;
|
||||
}
|
||||
if do_simplify_memories {
|
||||
top_module = simplify_memories(top_module);
|
||||
}
|
||||
Ok(top_module)
|
||||
}
|
||||
pub fn prepare_top_module<T: BundleType>(
|
||||
self,
|
||||
top_module: impl AsRef<Module<T>>,
|
||||
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
|
||||
self.prepare_top_module_helper(top_module.as_ref().canonical().intern())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ExportOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
simplify_memories: true,
|
||||
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
|
||||
simplify_memories: Self::default_simplify_memories(),
|
||||
simplify_enums: Self::default_simplify_enums(),
|
||||
__private: ExportOptionsPrivate(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_circuit_name(top_module_name_id: NameId) -> Interned<str> {
|
||||
let mut global_ns = Namespace::default();
|
||||
let circuit_name = global_ns.get(top_module_name_id);
|
||||
circuit_name.0
|
||||
}
|
||||
|
||||
pub fn export<T: BundleType, B: FileBackendTrait>(
|
||||
file_backend: B,
|
||||
top_module: &Module<T>,
|
||||
|
|
@ -2846,6 +2909,497 @@ pub fn export<T: BundleType, B: FileBackendTrait>(
|
|||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum ScalarizedModuleABIError {
|
||||
SimOnlyValuesAreNotPermitted,
|
||||
SimplifyEnumsError(SimplifyEnumsError),
|
||||
}
|
||||
|
||||
impl fmt::Display for ScalarizedModuleABIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted => {
|
||||
FirrtlError::SimOnlyValuesAreNotPermitted.fmt(f)
|
||||
}
|
||||
ScalarizedModuleABIError::SimplifyEnumsError(e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ScalarizedModuleABIError {}
|
||||
|
||||
impl From<SimplifyEnumsError> for ScalarizedModuleABIError {
|
||||
fn from(value: SimplifyEnumsError) -> Self {
|
||||
Self::SimplifyEnumsError(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ScalarizedModuleABIPortItem {
|
||||
Group(ScalarizedModuleABIPortGroup),
|
||||
Port(ScalarizedModuleABIPort),
|
||||
}
|
||||
|
||||
impl ScalarizedModuleABIPortItem {
|
||||
pub fn module_io(self) -> ModuleIO<CanonicalType> {
|
||||
*self
|
||||
.target()
|
||||
.base()
|
||||
.module_io()
|
||||
.expect("known to be ModuleIO")
|
||||
}
|
||||
pub fn target(self) -> Interned<Target> {
|
||||
match self {
|
||||
Self::Group(v) => v.target(),
|
||||
Self::Port(v) => v.target(),
|
||||
}
|
||||
}
|
||||
fn for_each_port_and_annotations_helper<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
&self,
|
||||
parent: Option<&ScalarizedModuleABIAnnotations<'_>>,
|
||||
f: &mut F,
|
||||
) -> ControlFlow<B> {
|
||||
match self {
|
||||
Self::Group(v) => v.for_each_port_and_annotations_helper(parent, f),
|
||||
Self::Port(port) => f(
|
||||
port,
|
||||
ScalarizedModuleABIAnnotations::new(parent, port.annotations.iter()),
|
||||
),
|
||||
}
|
||||
}
|
||||
pub fn for_each_port_and_annotations<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
self,
|
||||
mut f: F,
|
||||
) -> ControlFlow<B> {
|
||||
self.for_each_port_and_annotations_helper(None, &mut f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ScalarizedModuleABIPortItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Group(v) => v.fmt(f),
|
||||
Self::Port(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScalarizedModuleABIAnnotations<'a> {
|
||||
parent: Option<&'a ScalarizedModuleABIAnnotations<'a>>,
|
||||
parent_len: usize,
|
||||
annotations: std::slice::Iter<'a, TargetedAnnotation>,
|
||||
}
|
||||
|
||||
impl<'a> ScalarizedModuleABIAnnotations<'a> {
|
||||
fn new(
|
||||
parent: Option<&'a ScalarizedModuleABIAnnotations<'a>>,
|
||||
annotations: std::slice::Iter<'a, TargetedAnnotation>,
|
||||
) -> Self {
|
||||
Self {
|
||||
parent,
|
||||
parent_len: parent.map_or(0, |parent| parent.len()),
|
||||
annotations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for ScalarizedModuleABIAnnotations<'a> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
parent: None,
|
||||
parent_len: 0,
|
||||
annotations: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ScalarizedModuleABIAnnotations<'a> {
|
||||
type Item = &'a TargetedAnnotation;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let retval @ Some(_) = self.annotations.next() {
|
||||
break retval;
|
||||
}
|
||||
*self = self.parent?.clone();
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let len = self.len();
|
||||
(len, Some(len))
|
||||
}
|
||||
|
||||
fn fold<B, F>(mut self, mut init: B, mut f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
loop {
|
||||
let Self {
|
||||
parent,
|
||||
parent_len: _,
|
||||
annotations,
|
||||
} = self;
|
||||
init = annotations.fold(init, &mut f);
|
||||
let Some(next) = parent else {
|
||||
break;
|
||||
};
|
||||
self = next.clone();
|
||||
}
|
||||
init
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::FusedIterator for ScalarizedModuleABIAnnotations<'_> {}
|
||||
|
||||
impl ExactSizeIterator for ScalarizedModuleABIAnnotations<'_> {
|
||||
fn len(&self) -> usize {
|
||||
self.parent_len + self.annotations.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ScalarizedModuleABIPortGroup {
|
||||
target: Interned<Target>,
|
||||
common_annotations: Interned<[TargetedAnnotation]>,
|
||||
children: Interned<[ScalarizedModuleABIPortItem]>,
|
||||
}
|
||||
|
||||
impl ScalarizedModuleABIPortGroup {
|
||||
pub fn module_io(self) -> ModuleIO<CanonicalType> {
|
||||
*self
|
||||
.target
|
||||
.base()
|
||||
.module_io()
|
||||
.expect("known to be ModuleIO")
|
||||
}
|
||||
pub fn target(self) -> Interned<Target> {
|
||||
self.target
|
||||
}
|
||||
pub fn common_annotations(self) -> Interned<[TargetedAnnotation]> {
|
||||
self.common_annotations
|
||||
}
|
||||
pub fn children(self) -> Interned<[ScalarizedModuleABIPortItem]> {
|
||||
self.children
|
||||
}
|
||||
fn for_each_port_and_annotations_helper<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
&self,
|
||||
parent: Option<&ScalarizedModuleABIAnnotations<'_>>,
|
||||
f: &mut F,
|
||||
) -> ControlFlow<B> {
|
||||
let parent = ScalarizedModuleABIAnnotations::new(parent, self.common_annotations.iter());
|
||||
for item in &self.children {
|
||||
item.for_each_port_and_annotations_helper(Some(&parent), f)?;
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
pub fn for_each_port_and_annotations<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
self,
|
||||
mut f: F,
|
||||
) -> ControlFlow<B> {
|
||||
self.for_each_port_and_annotations_helper(None, &mut f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ScalarizedModuleABIPort {
|
||||
target: Interned<Target>,
|
||||
annotations: Interned<[TargetedAnnotation]>,
|
||||
scalarized_name: Interned<str>,
|
||||
}
|
||||
|
||||
impl ScalarizedModuleABIPort {
|
||||
pub fn module_io(self) -> ModuleIO<CanonicalType> {
|
||||
*self
|
||||
.target
|
||||
.base()
|
||||
.module_io()
|
||||
.expect("known to be ModuleIO")
|
||||
}
|
||||
pub fn target(self) -> Interned<Target> {
|
||||
self.target
|
||||
}
|
||||
pub fn annotations(self) -> Interned<[TargetedAnnotation]> {
|
||||
self.annotations
|
||||
}
|
||||
pub fn scalarized_name(self) -> Interned<str> {
|
||||
self.scalarized_name
|
||||
}
|
||||
}
|
||||
|
||||
enum ScalarizeTreeNodeBody {
|
||||
Leaf {
|
||||
scalarized_name: Interned<str>,
|
||||
},
|
||||
Bundle {
|
||||
ty: Bundle,
|
||||
fields: Vec<ScalarizeTreeNode>,
|
||||
},
|
||||
Array {
|
||||
elements: Vec<ScalarizeTreeNode>,
|
||||
},
|
||||
}
|
||||
|
||||
struct ScalarizeTreeNode {
|
||||
target: Interned<Target>,
|
||||
annotations: Vec<TargetedAnnotation>,
|
||||
body: ScalarizeTreeNodeBody,
|
||||
}
|
||||
|
||||
impl ScalarizeTreeNode {
|
||||
#[track_caller]
|
||||
fn find_target(&mut self, annotation_target: Interned<Target>) -> &mut Self {
|
||||
match *annotation_target {
|
||||
Target::Base(_) => {
|
||||
assert_eq!(
|
||||
annotation_target, self.target,
|
||||
"annotation not on correct ModuleIO",
|
||||
);
|
||||
self
|
||||
}
|
||||
Target::Child(target_child) => {
|
||||
let parent = self.find_target(target_child.parent());
|
||||
match *target_child.path_element() {
|
||||
TargetPathElement::BundleField(TargetPathBundleField { name }) => {
|
||||
match parent.body {
|
||||
ScalarizeTreeNodeBody::Leaf { .. } => parent,
|
||||
ScalarizeTreeNodeBody::Bundle { ty, ref mut fields } => {
|
||||
&mut fields[ty.name_indexes()[&name]]
|
||||
}
|
||||
ScalarizeTreeNodeBody::Array { .. } => {
|
||||
unreachable!("types are known to match")
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => {
|
||||
match parent.body {
|
||||
ScalarizeTreeNodeBody::Leaf { .. } => parent,
|
||||
ScalarizeTreeNodeBody::Bundle { .. } => {
|
||||
unreachable!("types are known to match")
|
||||
}
|
||||
ScalarizeTreeNodeBody::Array { ref mut elements } => {
|
||||
&mut elements[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetPathElement::DynArrayElement(_) => {
|
||||
unreachable!("annotations are only on static targets");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn into_scalarized_item(self) -> ScalarizedModuleABIPortItem {
|
||||
let Self {
|
||||
target,
|
||||
annotations,
|
||||
body,
|
||||
} = self;
|
||||
match body {
|
||||
ScalarizeTreeNodeBody::Leaf { scalarized_name } => {
|
||||
ScalarizedModuleABIPortItem::Port(ScalarizedModuleABIPort {
|
||||
target,
|
||||
annotations: Intern::intern_owned(annotations),
|
||||
scalarized_name,
|
||||
})
|
||||
}
|
||||
ScalarizeTreeNodeBody::Bundle { fields: items, .. }
|
||||
| ScalarizeTreeNodeBody::Array { elements: items } => {
|
||||
ScalarizedModuleABIPortItem::Group(ScalarizedModuleABIPortGroup {
|
||||
target,
|
||||
common_annotations: Intern::intern_owned(annotations),
|
||||
children: Interned::from_iter(
|
||||
items.into_iter().map(Self::into_scalarized_item),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ScalarizeTreeBuilder {
|
||||
scalarized_ns: Namespace,
|
||||
type_state: TypeState,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl ScalarizeTreeBuilder {
|
||||
#[track_caller]
|
||||
fn build_bundle(
|
||||
&mut self,
|
||||
target: Interned<Target>,
|
||||
ty: Bundle,
|
||||
) -> Result<ScalarizeTreeNode, ScalarizedModuleABIError> {
|
||||
let mut fields = Vec::with_capacity(ty.fields().len());
|
||||
let original_len = self.name.len();
|
||||
for BundleField { name, .. } in ty.fields() {
|
||||
let firrtl_name = self
|
||||
.type_state
|
||||
.get_bundle_field(ty, name)
|
||||
.map_err(|e| match e {
|
||||
FirrtlError::SimOnlyValuesAreNotPermitted => {
|
||||
ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted
|
||||
}
|
||||
})?
|
||||
.0;
|
||||
write!(self.name, "_{firrtl_name}").expect("writing to String is infallible");
|
||||
fields.push(
|
||||
self.build(
|
||||
target
|
||||
.join(TargetPathElement::intern_sized(
|
||||
TargetPathBundleField { name }.into(),
|
||||
))
|
||||
.intern_sized(),
|
||||
)?,
|
||||
);
|
||||
self.name.truncate(original_len);
|
||||
}
|
||||
Ok(ScalarizeTreeNode {
|
||||
target,
|
||||
annotations: Vec::new(),
|
||||
body: ScalarizeTreeNodeBody::Bundle { ty, fields },
|
||||
})
|
||||
}
|
||||
#[track_caller]
|
||||
fn build(
|
||||
&mut self,
|
||||
target: Interned<Target>,
|
||||
) -> Result<ScalarizeTreeNode, ScalarizedModuleABIError> {
|
||||
Ok(match target.canonical_ty() {
|
||||
CanonicalType::UInt(_)
|
||||
| CanonicalType::SInt(_)
|
||||
| CanonicalType::Bool(_)
|
||||
| CanonicalType::Enum(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::Clock(_) => {
|
||||
let scalarized_name = self.scalarized_ns.get(str::intern(&self.name)).0;
|
||||
ScalarizeTreeNode {
|
||||
target,
|
||||
annotations: Vec::new(),
|
||||
body: ScalarizeTreeNodeBody::Leaf { scalarized_name },
|
||||
}
|
||||
}
|
||||
CanonicalType::Array(ty) => {
|
||||
let mut elements = Vec::with_capacity(ty.len());
|
||||
let original_len = self.name.len();
|
||||
for index in 0..ty.len() {
|
||||
write!(self.name, "_{index}").expect("writing to String is infallible");
|
||||
elements.push(
|
||||
self.build(
|
||||
target
|
||||
.join(TargetPathElement::intern_sized(
|
||||
TargetPathArrayElement { index }.into(),
|
||||
))
|
||||
.intern_sized(),
|
||||
)?,
|
||||
);
|
||||
self.name.truncate(original_len);
|
||||
}
|
||||
ScalarizeTreeNode {
|
||||
target,
|
||||
annotations: Vec::new(),
|
||||
body: ScalarizeTreeNodeBody::Array { elements },
|
||||
}
|
||||
}
|
||||
CanonicalType::Bundle(ty) => self.build_bundle(target, ty)?,
|
||||
CanonicalType::PhantomConst(_) => {
|
||||
self.build_bundle(target, Bundle::new(Interned::default()))?
|
||||
}
|
||||
CanonicalType::DynSimOnly(_) => {
|
||||
return Err(ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ScalarizedModuleABI {
|
||||
module_io: Interned<[AnnotatedModuleIO]>,
|
||||
items: Interned<[ScalarizedModuleABIPortItem]>,
|
||||
}
|
||||
|
||||
impl ScalarizedModuleABI {
|
||||
#[track_caller]
|
||||
fn from_io(module_io: Interned<[AnnotatedModuleIO]>) -> Result<Self, ScalarizedModuleABIError> {
|
||||
let mut firrtl_ns = Namespace::default();
|
||||
let mut tree_builder = ScalarizeTreeBuilder::default();
|
||||
let mut items = Vec::new();
|
||||
for module_io in module_io {
|
||||
let firrtl_name = firrtl_ns.get(module_io.module_io.name_id());
|
||||
tree_builder.name.clear();
|
||||
tree_builder.name.push_str(&firrtl_name.0);
|
||||
let mut tree = tree_builder.build(Target::from(module_io.module_io).intern_sized())?;
|
||||
for annotation in module_io.annotations {
|
||||
tree.find_target(annotation.target())
|
||||
.annotations
|
||||
.push(annotation);
|
||||
}
|
||||
items.push(tree.into_scalarized_item());
|
||||
}
|
||||
Ok(Self {
|
||||
module_io,
|
||||
items: Intern::intern_owned(items),
|
||||
})
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new<T: BundleType>(
|
||||
module: impl AsRef<Module<T>>,
|
||||
options: ExportOptions,
|
||||
) -> Result<Self, ScalarizedModuleABIError> {
|
||||
Self::from_io(options.prepare_top_module(module)?.module_io())
|
||||
}
|
||||
pub fn module_io(&self) -> Interned<[AnnotatedModuleIO]> {
|
||||
self.module_io
|
||||
}
|
||||
pub fn items(&self) -> Interned<[ScalarizedModuleABIPortItem]> {
|
||||
self.items
|
||||
}
|
||||
pub fn for_each_port_and_annotations<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
self,
|
||||
mut f: F,
|
||||
) -> ControlFlow<B> {
|
||||
for item in &self.items {
|
||||
item.for_each_port_and_annotations_helper(None, &mut f)?;
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn assert_export_firrtl_impl<T: BundleType>(top_module: &Module<T>, expected: TestBackend) {
|
||||
|
|
|
|||
|
|
@ -4,16 +4,19 @@
|
|||
use crate::{
|
||||
array::ArrayType,
|
||||
expr::{
|
||||
Expr, NotALiteralExpr, ToExpr, ToLiteralBits,
|
||||
Expr, NotALiteralExpr, ToExpr, ToLiteralBits, ToSimValueInner, ToValueless, ValueType,
|
||||
Valueless,
|
||||
target::{GetTarget, Target},
|
||||
value_category::ValueCategoryValue,
|
||||
},
|
||||
hdl,
|
||||
intern::{Intern, Interned, Memoize},
|
||||
sim::value::{SimValue, ToSimValueWithType},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
||||
OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
||||
CanonicalType, FillInDefaultedGenerics, OpaqueSimValueSize, OpaqueSimValueSlice,
|
||||
OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, TypeProperties,
|
||||
impl_match_variant_as_self,
|
||||
},
|
||||
util::{ConstBool, ConstUsize, GenericConstBool, GenericConstUsize, interned_bit, slice_range},
|
||||
};
|
||||
|
|
@ -31,7 +34,7 @@ use std::{
|
|||
num::NonZero,
|
||||
ops::{Index, Not, Range, RangeBounds, RangeInclusive},
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
|
||||
mod uint_in_range;
|
||||
|
|
@ -60,8 +63,62 @@ mod sealed {
|
|||
pub const DYN_SIZE: usize = !0;
|
||||
pub type DynSize = ConstUsize<DYN_SIZE>;
|
||||
|
||||
trait KnownSizeBaseSealed {}
|
||||
|
||||
impl<const N: usize> KnownSizeBaseSealed for [(); N] {}
|
||||
|
||||
#[expect(private_bounds)]
|
||||
pub trait KnownSizeBase: KnownSizeBaseSealed {}
|
||||
|
||||
macro_rules! impl_known_size_base {
|
||||
($($size:literal),* $(,)?) => {
|
||||
$(impl KnownSizeBase for [(); $size] {})*
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl_known_size_base! {
|
||||
0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F,
|
||||
0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, 0x018, 0x019, 0x01A, 0x01B, 0x01C, 0x01D, 0x01E, 0x01F,
|
||||
0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x027, 0x028, 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F,
|
||||
0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F,
|
||||
0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047, 0x048, 0x049, 0x04A, 0x04B, 0x04C, 0x04D, 0x04E, 0x04F,
|
||||
0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, 0x058, 0x059, 0x05A, 0x05B, 0x05C, 0x05D, 0x05E, 0x05F,
|
||||
0x060, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, 0x068, 0x069, 0x06A, 0x06B, 0x06C, 0x06D, 0x06E, 0x06F,
|
||||
0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, 0x078, 0x079, 0x07A, 0x07B, 0x07C, 0x07D, 0x07E, 0x07F,
|
||||
0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087, 0x088, 0x089, 0x08A, 0x08B, 0x08C, 0x08D, 0x08E, 0x08F,
|
||||
0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097, 0x098, 0x099, 0x09A, 0x09B, 0x09C, 0x09D, 0x09E, 0x09F,
|
||||
0x0A0, 0x0A1, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0AA, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x0AF,
|
||||
0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0BA, 0x0BB, 0x0BC, 0x0BD, 0x0BE, 0x0BF,
|
||||
0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x0C7, 0x0C8, 0x0C9, 0x0CA, 0x0CB, 0x0CC, 0x0CD, 0x0CE, 0x0CF,
|
||||
0x0D0, 0x0D1, 0x0D2, 0x0D3, 0x0D4, 0x0D5, 0x0D6, 0x0D7, 0x0D8, 0x0D9, 0x0DA, 0x0DB, 0x0DC, 0x0DD, 0x0DE, 0x0DF,
|
||||
0x0E0, 0x0E1, 0x0E2, 0x0E3, 0x0E4, 0x0E5, 0x0E6, 0x0E7, 0x0E8, 0x0E9, 0x0EA, 0x0EB, 0x0EC, 0x0ED, 0x0EE, 0x0EF,
|
||||
0x0F0, 0x0F1, 0x0F2, 0x0F3, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x0F9, 0x0FA, 0x0FB, 0x0FC, 0x0FD, 0x0FE, 0x0FF,
|
||||
0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10A, 0x10B, 0x10C, 0x10D, 0x10E, 0x10F,
|
||||
0x110, 0x111, 0x112, 0x113, 0x114, 0x115, 0x116, 0x117, 0x118, 0x119, 0x11A, 0x11B, 0x11C, 0x11D, 0x11E, 0x11F,
|
||||
0x120, 0x121, 0x122, 0x123, 0x124, 0x125, 0x126, 0x127, 0x128, 0x129, 0x12A, 0x12B, 0x12C, 0x12D, 0x12E, 0x12F,
|
||||
0x130, 0x131, 0x132, 0x133, 0x134, 0x135, 0x136, 0x137, 0x138, 0x139, 0x13A, 0x13B, 0x13C, 0x13D, 0x13E, 0x13F,
|
||||
0x140, 0x141, 0x142, 0x143, 0x144, 0x145, 0x146, 0x147, 0x148, 0x149, 0x14A, 0x14B, 0x14C, 0x14D, 0x14E, 0x14F,
|
||||
0x150, 0x151, 0x152, 0x153, 0x154, 0x155, 0x156, 0x157, 0x158, 0x159, 0x15A, 0x15B, 0x15C, 0x15D, 0x15E, 0x15F,
|
||||
0x160, 0x161, 0x162, 0x163, 0x164, 0x165, 0x166, 0x167, 0x168, 0x169, 0x16A, 0x16B, 0x16C, 0x16D, 0x16E, 0x16F,
|
||||
0x170, 0x171, 0x172, 0x173, 0x174, 0x175, 0x176, 0x177, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x17F,
|
||||
0x180, 0x181, 0x182, 0x183, 0x184, 0x185, 0x186, 0x187, 0x188, 0x189, 0x18A, 0x18B, 0x18C, 0x18D, 0x18E, 0x18F,
|
||||
0x190, 0x191, 0x192, 0x193, 0x194, 0x195, 0x196, 0x197, 0x198, 0x199, 0x19A, 0x19B, 0x19C, 0x19D, 0x19E, 0x19F,
|
||||
0x1A0, 0x1A1, 0x1A2, 0x1A3, 0x1A4, 0x1A5, 0x1A6, 0x1A7, 0x1A8, 0x1A9, 0x1AA, 0x1AB, 0x1AC, 0x1AD, 0x1AE, 0x1AF,
|
||||
0x1B0, 0x1B1, 0x1B2, 0x1B3, 0x1B4, 0x1B5, 0x1B6, 0x1B7, 0x1B8, 0x1B9, 0x1BA, 0x1BB, 0x1BC, 0x1BD, 0x1BE, 0x1BF,
|
||||
0x1C0, 0x1C1, 0x1C2, 0x1C3, 0x1C4, 0x1C5, 0x1C6, 0x1C7, 0x1C8, 0x1C9, 0x1CA, 0x1CB, 0x1CC, 0x1CD, 0x1CE, 0x1CF,
|
||||
0x1D0, 0x1D1, 0x1D2, 0x1D3, 0x1D4, 0x1D5, 0x1D6, 0x1D7, 0x1D8, 0x1D9, 0x1DA, 0x1DB, 0x1DC, 0x1DD, 0x1DE, 0x1DF,
|
||||
0x1E0, 0x1E1, 0x1E2, 0x1E3, 0x1E4, 0x1E5, 0x1E6, 0x1E7, 0x1E8, 0x1E9, 0x1EA, 0x1EB, 0x1EC, 0x1ED, 0x1EE, 0x1EF,
|
||||
0x1F0, 0x1F1, 0x1F2, 0x1F3, 0x1F4, 0x1F5, 0x1F6, 0x1F7, 0x1F8, 0x1F9, 0x1FA, 0x1FB, 0x1FC, 0x1FD, 0x1FE, 0x1FF,
|
||||
0x200,
|
||||
}
|
||||
|
||||
pub trait KnownSize:
|
||||
GenericConstUsize + sealed::SizeTypeSealed + sealed::SizeSealed + Default
|
||||
GenericConstUsize
|
||||
+ sealed::SizeTypeSealed
|
||||
+ sealed::SizeSealed
|
||||
+ Default
|
||||
+ FillInDefaultedGenerics<Type = Self>
|
||||
{
|
||||
const SIZE: Self;
|
||||
type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]>
|
||||
|
|
@ -89,34 +146,14 @@ pub trait KnownSize:
|
|||
+ ToSimValueWithType<ArrayType<Element, Self>>;
|
||||
}
|
||||
|
||||
macro_rules! known_widths {
|
||||
([] $($bits:literal)+) => {
|
||||
impl KnownSize for ConstUsize<{
|
||||
let v = 0;
|
||||
$(let v = v * 2 + $bits;)*
|
||||
v
|
||||
}> {
|
||||
impl<const N: usize> KnownSize for ConstUsize<N>
|
||||
where
|
||||
[(); N]: KnownSizeBase,
|
||||
{
|
||||
const SIZE: Self = Self;
|
||||
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
|
||||
type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE];
|
||||
type ArrayMatch<Element: Type> = [Expr<Element>; N];
|
||||
type ArraySimValue<Element: Type> = [SimValue<Element>; N];
|
||||
}
|
||||
};
|
||||
([2 $($rest:tt)*] $($bits:literal)+) => {
|
||||
known_widths!([$($rest)*] $($bits)* 0);
|
||||
known_widths!([$($rest)*] $($bits)* 1);
|
||||
};
|
||||
([2 $($rest:tt)*]) => {
|
||||
known_widths!([$($rest)*] 0);
|
||||
known_widths!([$($rest)*] 1);
|
||||
impl KnownSize for ConstUsize<{2 $(* $rest)*}> {
|
||||
const SIZE: Self = Self;
|
||||
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
|
||||
type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
known_widths!([2 2 2 2 2 2 2 2 2]);
|
||||
|
||||
pub trait SizeType:
|
||||
sealed::SizeTypeSealed
|
||||
|
|
@ -129,6 +166,7 @@ pub trait SizeType:
|
|||
+ 'static
|
||||
+ Serialize
|
||||
+ DeserializeOwned
|
||||
+ FillInDefaultedGenerics<Type = Self>
|
||||
{
|
||||
type Size: Size<SizeType = Self>;
|
||||
}
|
||||
|
|
@ -530,6 +568,23 @@ fn deserialize_int_value<'de, D: Deserializer<'de>>(
|
|||
})
|
||||
}
|
||||
|
||||
macro_rules! impl_valueless_op_forward {
|
||||
(
|
||||
#[forward_to = $ForwardTrait:path]
|
||||
impl<$($G:ident: $Bound:path),*> $TraitPath:ident$(::$TraitPathRest:ident)+<$($Rhs:ty)?> for $SelfTy:ty {
|
||||
$($(type $Output:ident;)?
|
||||
use $forward_fn:path as $fn:ident($self:ident $(, $rhs:ident)?);)?
|
||||
}
|
||||
) => {
|
||||
impl<$($G: $Bound),*> $TraitPath$(::$TraitPathRest)+<$($Rhs)?> for $SelfTy {
|
||||
$($(type $Output = <$SelfTy as $ForwardTrait>::$Output;)?
|
||||
fn $fn($self $(, $rhs: $Rhs)?) $(-> Self::$Output)? {
|
||||
$forward_fn($self $(, $rhs)?)
|
||||
})?
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_int {
|
||||
($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
@ -582,6 +637,176 @@ macro_rules! impl_int {
|
|||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Add<Valueless<$name<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output = Valueless<$name>;
|
||||
|
||||
fn add(self, rhs: Valueless<$name<RhsWidth>>) -> Self::Output {
|
||||
Valueless::new($name::new_dyn(
|
||||
self.ty()
|
||||
.width()
|
||||
.max(rhs.ty().width())
|
||||
.checked_add(1)
|
||||
.expect("width too big"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl_valueless_op_forward! {
|
||||
#[forward_to = std::ops::Add<Valueless<$name<RhsWidth>>>]
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Sub<Valueless<$name<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output;
|
||||
|
||||
use std::ops::Add::<Valueless<$name<RhsWidth>>>::add as sub(self, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::BitOr<Valueless<$name<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output = Valueless<UInt>;
|
||||
|
||||
fn bitor(self, rhs: Valueless<$name<RhsWidth>>) -> Self::Output {
|
||||
Valueless::new(UInt::new_dyn(self.ty().width().max(rhs.ty().width())))
|
||||
}
|
||||
}
|
||||
|
||||
impl_valueless_op_forward! {
|
||||
#[forward_to = std::ops::BitOr<Valueless<$name<RhsWidth>>>]
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::BitAnd<Valueless<$name<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output;
|
||||
|
||||
use std::ops::BitOr::<Valueless<$name<RhsWidth>>>::bitor as bitand(self, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl_valueless_op_forward! {
|
||||
#[forward_to = std::ops::BitOr<Valueless<$name<RhsWidth>>>]
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::BitXor<Valueless<$name<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output;
|
||||
|
||||
use std::ops::BitOr::<Valueless<$name<RhsWidth>>>::bitor as bitxor(self, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> std::ops::Not for Valueless<$name<Width>> {
|
||||
type Output = Valueless<UIntType<Width>>;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
Valueless::new(self.ty().as_same_width_uint())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> std::ops::Not for $value<Width> {
|
||||
type Output = UIntValue<Width>;
|
||||
|
||||
fn not(mut self) -> Self::Output {
|
||||
// modifies in-place despite using `Not::not`
|
||||
let _ = Not::not(self.bits_mut());
|
||||
UIntValue::new(self.into_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> std::ops::Not for &'_ $value<Width> {
|
||||
type Output = UIntValue<Width>;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
$value::not(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Mul<Valueless<$name<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output = Valueless<$name>;
|
||||
|
||||
fn mul(self, rhs: Valueless<$name<RhsWidth>>) -> Self::Output {
|
||||
Valueless::new($name::new_dyn(
|
||||
self.ty()
|
||||
.width()
|
||||
.checked_add(rhs.ty().width())
|
||||
.expect("width too big"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Rem<Valueless<$name<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output = Valueless<$name>;
|
||||
|
||||
fn rem(self, rhs: Valueless<$name<RhsWidth>>) -> Self::Output {
|
||||
Valueless::new($name::new_dyn(self.ty().width().min(rhs.ty().width())))
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Shl<Valueless<UIntType<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output = Valueless<$name>;
|
||||
|
||||
#[track_caller]
|
||||
fn shl(self, rhs: Valueless<UIntType<RhsWidth>>) -> Self::Output {
|
||||
let Some(pow2_rhs_width) = rhs
|
||||
.ty()
|
||||
.width()
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|v| 2usize.checked_pow(v))
|
||||
else {
|
||||
panic!(
|
||||
"dynamic left-shift amount's bit-width is too big, try casting the shift \
|
||||
amount to a smaller bit-width before shifting"
|
||||
);
|
||||
};
|
||||
Valueless::new($name::new_dyn(
|
||||
(pow2_rhs_width - 1)
|
||||
.checked_add(self.ty().width())
|
||||
.expect("width too big"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Shr<Valueless<UIntType<RhsWidth>>>
|
||||
for Valueless<$name<LhsWidth>>
|
||||
{
|
||||
type Output = Valueless<$name<LhsWidth>>;
|
||||
|
||||
fn shr(self, _rhs: Valueless<UIntType<RhsWidth>>) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size> std::ops::Shl<usize> for Valueless<$name<LhsWidth>> {
|
||||
type Output = Valueless<$name>;
|
||||
|
||||
fn shl(self, rhs: usize) -> Self::Output {
|
||||
Valueless::new($name::new_dyn(
|
||||
self.ty().width().checked_add(rhs).expect("width too big"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size> std::ops::Shr<usize> for Valueless<$name<LhsWidth>> {
|
||||
type Output = Valueless<$name>;
|
||||
|
||||
fn shr(self, rhs: usize) -> Self::Output {
|
||||
let width = self.ty().width().saturating_sub(rhs);
|
||||
Valueless::new($name::new_dyn(if $SIGNED && width == 0 {
|
||||
1
|
||||
} else {
|
||||
width
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> sealed::BoolOrIntTypeSealed for $name<Width> {}
|
||||
|
||||
impl<Width: Size> BoolOrIntType for $name<Width> {
|
||||
|
|
@ -843,6 +1068,12 @@ macro_rules! impl_int {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Width: Size> From<&'a $value<Width>> for BigInt {
|
||||
fn from(v: &'a $value<Width>) -> BigInt {
|
||||
v.to_bigint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> $value<Width> {
|
||||
pub fn width(&self) -> usize {
|
||||
if let Some(retval) = Width::KNOWN_VALUE {
|
||||
|
|
@ -861,11 +1092,6 @@ macro_rules! impl_int {
|
|||
pub fn into_bits(self) -> Arc<BitVec> {
|
||||
self.bits
|
||||
}
|
||||
pub fn ty(&self) -> $name<Width> {
|
||||
$name {
|
||||
width: Width::from_usize(self.width()),
|
||||
}
|
||||
}
|
||||
pub fn as_dyn_int(self) -> $value {
|
||||
$value {
|
||||
bits: self.bits,
|
||||
|
|
@ -878,6 +1104,38 @@ macro_rules! impl_int {
|
|||
pub fn bits_mut(&mut self) -> &mut BitSlice {
|
||||
Arc::<BitVec>::make_mut(&mut self.bits)
|
||||
}
|
||||
pub fn hdl_cmp<R: Size>(&self, other: &$value<R>) -> std::cmp::Ordering {
|
||||
self.to_bigint().cmp(&other.to_bigint())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> ValueType for $value<Width> {
|
||||
type Type = $name<Width>;
|
||||
type ValueCategory = ValueCategoryValue;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
$name {
|
||||
width: Width::from_usize(self.width()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Width: Size> ToSimValueInner<'a> for $value<Width> {
|
||||
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
|
||||
Cow::Borrowed(this)
|
||||
}
|
||||
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
|
||||
Cow::Owned(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Width: Size> ToSimValueInner<'a> for &'a $value<Width> {
|
||||
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
|
||||
Cow::Borrowed(&**this)
|
||||
}
|
||||
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
|
||||
Cow::Borrowed(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> ToLiteralBits for $value<Width> {
|
||||
|
|
@ -1015,11 +1273,58 @@ impl SInt {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_prim_int {
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Div<Valueless<UIntType<RhsWidth>>>
|
||||
for Valueless<UIntType<LhsWidth>>
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, _rhs: Valueless<UIntType<RhsWidth>>) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Div<Valueless<SIntType<RhsWidth>>>
|
||||
for Valueless<SIntType<LhsWidth>>
|
||||
{
|
||||
type Output = Valueless<SInt>;
|
||||
|
||||
fn div(self, _rhs: Valueless<SIntType<RhsWidth>>) -> Self::Output {
|
||||
Valueless::new(SInt::new_dyn(
|
||||
self.ty().width().checked_add(1).expect("width too big"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> std::ops::Neg for Valueless<SIntType<Width>> {
|
||||
type Output = Valueless<SInt>;
|
||||
fn neg(self) -> Self::Output {
|
||||
Valueless::new(SInt::new_dyn(
|
||||
self.ty().width().checked_add(1).expect("width too big"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> std::ops::Neg for SIntValue<Width> {
|
||||
type Output = SIntValue;
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> std::ops::Neg for &'_ SIntValue<Width> {
|
||||
type Output = SIntValue;
|
||||
fn neg(self) -> Self::Output {
|
||||
let ty = self.to_valueless().neg().ty();
|
||||
ty.from_bigint_wrapping(&-self.to_bigint())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_prim_int_to_expr {
|
||||
(
|
||||
$(#[$meta:meta])*
|
||||
$prim_int:ident, $ty:ty
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
impl From<$prim_int> for <$ty as BoolOrIntType>::Value {
|
||||
fn from(v: $prim_int) -> Self {
|
||||
<$ty>::le_bytes_to_value_wrapping(
|
||||
|
|
@ -1028,15 +1333,32 @@ macro_rules! impl_prim_int {
|
|||
)
|
||||
}
|
||||
}
|
||||
$(#[$meta])*
|
||||
impl From<NonZero<$prim_int>> for <$ty as BoolOrIntType>::Value {
|
||||
fn from(v: NonZero<$prim_int>) -> Self {
|
||||
v.get().into()
|
||||
}
|
||||
}
|
||||
$(#[$meta])*
|
||||
impl ToExpr for $prim_int {
|
||||
impl<'a> ToSimValueInner<'a> for $prim_int {
|
||||
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
|
||||
Self::into_sim_value_inner(*this)
|
||||
}
|
||||
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
|
||||
Cow::Owned(this.into())
|
||||
}
|
||||
}
|
||||
$(#[$meta])*
|
||||
impl ValueType for $prim_int {
|
||||
type Type = $ty;
|
||||
type ValueCategory = ValueCategoryValue;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
StaticType::TYPE
|
||||
}
|
||||
}
|
||||
$(#[$meta])*
|
||||
impl ToExpr for $prim_int {
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
<$ty>::le_bytes_to_expr_wrapping(
|
||||
&self.to_le_bytes(),
|
||||
|
|
@ -1045,9 +1367,25 @@ macro_rules! impl_prim_int {
|
|||
}
|
||||
}
|
||||
$(#[$meta])*
|
||||
impl ToExpr for NonZero<$prim_int> {
|
||||
impl<'a> ToSimValueInner<'a> for NonZero<$prim_int> {
|
||||
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
|
||||
Self::into_sim_value_inner(*this)
|
||||
}
|
||||
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
|
||||
Cow::Owned(this.into())
|
||||
}
|
||||
}
|
||||
$(#[$meta])*
|
||||
impl ValueType for NonZero<$prim_int> {
|
||||
type Type = $ty;
|
||||
type ValueCategory = ValueCategoryValue;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
StaticType::TYPE
|
||||
}
|
||||
}
|
||||
$(#[$meta])*
|
||||
impl ToExpr for NonZero<$prim_int> {
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
self.get().to_expr()
|
||||
}
|
||||
|
|
@ -1055,28 +1393,208 @@ macro_rules! impl_prim_int {
|
|||
};
|
||||
}
|
||||
|
||||
impl_prim_int!(u8, UInt<8>);
|
||||
impl_prim_int!(u16, UInt<16>);
|
||||
impl_prim_int!(u32, UInt<32>);
|
||||
impl_prim_int!(u64, UInt<64>);
|
||||
impl_prim_int!(u128, UInt<128>);
|
||||
impl_prim_int!(i8, SInt<8>);
|
||||
impl_prim_int!(i16, SInt<16>);
|
||||
impl_prim_int!(i32, SInt<32>);
|
||||
impl_prim_int!(i64, SInt<64>);
|
||||
impl_prim_int!(i128, SInt<128>);
|
||||
|
||||
impl_prim_int!(
|
||||
macro_rules! impl_for_all_prim_uints {
|
||||
(
|
||||
#[size =, $(rest:tt)*]
|
||||
$m:ident!()
|
||||
) => {
|
||||
impl_for_all_prim_uints!(
|
||||
#[usize =, $($rest)*]
|
||||
$m!()
|
||||
);
|
||||
};
|
||||
(
|
||||
#[size = size, $(rest:tt)*]
|
||||
$m:ident!()
|
||||
) => {
|
||||
impl_for_all_prim_uints!(
|
||||
#[usize = usize, $($rest)*]
|
||||
$m!()
|
||||
);
|
||||
};
|
||||
(
|
||||
#[usize = $($usize:ident)?, NonZero = $NonZero:ident]
|
||||
$m:ident!()
|
||||
) => {
|
||||
impl_for_all_prim_uints!(
|
||||
#[usize = $($usize)?, NonZero = $NonZero, NonZero<usize> = $((NonZero<$usize>))?]
|
||||
$m!()
|
||||
);
|
||||
};
|
||||
(
|
||||
#[usize = $($usize:ident)?, NonZero =]
|
||||
$m:ident!()
|
||||
) => {
|
||||
impl_for_all_prim_uints!(
|
||||
#[usize = $($usize)?, NonZero =, NonZero<usize> =]
|
||||
$m!()
|
||||
);
|
||||
};
|
||||
(
|
||||
#[usize = $($usize:ident)?, NonZero = $($NonZero:ident)?, NonZero<usize> = $(($($NonZeroUsize:tt)*))?]
|
||||
$m:ident!()
|
||||
) => {
|
||||
$m!(u8, UInt<8>);
|
||||
$m!(u16, UInt<16>);
|
||||
$m!(u32, UInt<32>);
|
||||
$m!(u64, UInt<64>);
|
||||
$m!(u128, UInt<128>);
|
||||
$($m!($NonZero<u8>, UInt<8>);)?
|
||||
$($m!($NonZero<u16>, UInt<16>);)?
|
||||
$($m!($NonZero<u32>, UInt<32>);)?
|
||||
$($m!($NonZero<u64>, UInt<64>);)?
|
||||
$($m!($NonZero<u128>, UInt<128>);)?
|
||||
$($m!(
|
||||
/// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
|
||||
usize, UInt<64>
|
||||
);
|
||||
$usize, UInt<64>
|
||||
);)?
|
||||
$($m!(
|
||||
/// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
|
||||
$($NonZeroUsize)*, UInt<64>
|
||||
);)?
|
||||
};
|
||||
}
|
||||
|
||||
impl_prim_int!(
|
||||
macro_rules! impl_for_all_prim_sints {
|
||||
(
|
||||
#[size =, $(rest:tt)*]
|
||||
$m:ident!()
|
||||
) => {
|
||||
impl_for_all_prim_sints!(
|
||||
#[isize =, $($rest)*]
|
||||
$m!()
|
||||
);
|
||||
};
|
||||
(
|
||||
#[size = size, $(rest:tt)*]
|
||||
$m:ident!()
|
||||
) => {
|
||||
impl_for_all_prim_sints!(
|
||||
#[isize = isize, $($rest)*]
|
||||
$m!()
|
||||
);
|
||||
};
|
||||
(
|
||||
#[isize = $($isize:ident)?, NonZero = $NonZero:ident]
|
||||
$m:ident!()
|
||||
) => {
|
||||
impl_for_all_prim_sints!(
|
||||
#[isize = $($isize)?, NonZero = $NonZero, NonZero<isize> = $((NonZero<$isize>))?]
|
||||
$m!()
|
||||
);
|
||||
};
|
||||
(
|
||||
#[isize = $($isize:ident)?, NonZero =]
|
||||
$m:ident!()
|
||||
) => {
|
||||
impl_for_all_prim_sints!(
|
||||
#[isize = $($isize)?, NonZero =, NonZero<isize> =]
|
||||
$m!()
|
||||
);
|
||||
};
|
||||
(
|
||||
#[isize = $($isize:ident)?, NonZero = $($NonZero:ident)?, NonZero<isize> = $(($($NonZeroIsize:tt)*))?]
|
||||
$m:ident!()
|
||||
) => {
|
||||
$m!(i8, SInt<8>);
|
||||
$m!(i16, SInt<16>);
|
||||
$m!(i32, SInt<32>);
|
||||
$m!(i64, SInt<64>);
|
||||
$m!(i128, SInt<128>);
|
||||
$($m!($NonZero<i8>, SInt<8>);)?
|
||||
$($m!($NonZero<i16>, SInt<16>);)?
|
||||
$($m!($NonZero<i32>, SInt<32>);)?
|
||||
$($m!($NonZero<i64>, SInt<64>);)?
|
||||
$($m!($NonZero<i128>, SInt<128>);)?
|
||||
$($m!(
|
||||
/// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
|
||||
isize, SInt<64>
|
||||
$isize, SInt<64>
|
||||
);)?
|
||||
$($m!(
|
||||
/// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
|
||||
$($NonZeroIsize)*, SInt<64>
|
||||
);)?
|
||||
};
|
||||
}
|
||||
|
||||
impl_for_all_prim_uints!(
|
||||
#[usize = usize, NonZero =]
|
||||
impl_prim_int_to_expr!()
|
||||
);
|
||||
|
||||
pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
|
||||
impl_for_all_prim_sints!(
|
||||
#[isize = isize, NonZero =]
|
||||
impl_prim_int_to_expr!()
|
||||
);
|
||||
|
||||
macro_rules! impl_prim_int_from_value {
|
||||
($prim_int:ident, UInt<$width:literal>) => {
|
||||
impl_prim_int_from_value!($prim_int, UInt<$width>, UIntValue<ConstUsize<$width>>);
|
||||
};
|
||||
($prim_int:ident, SInt<$width:literal>) => {
|
||||
impl_prim_int_from_value!($prim_int, SInt<$width>, SIntValue<ConstUsize<$width>>);
|
||||
};
|
||||
($prim_int:ident, $ty:ty, $value:ty) => {
|
||||
impl From<$value> for $prim_int {
|
||||
fn from(value: $value) -> Self {
|
||||
value.as_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut $value> for $prim_int {
|
||||
fn from(value: &'a mut $value) -> Self {
|
||||
value.as_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SimValue<$ty>> for $prim_int {
|
||||
fn from(value: SimValue<$ty>) -> Self {
|
||||
value.as_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut SimValue<$ty>> for $prim_int {
|
||||
fn from(value: &'a mut SimValue<$ty>) -> Self {
|
||||
value.as_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a SimValue<$ty>> for $prim_int {
|
||||
fn from(value: &'a SimValue<$ty>) -> Self {
|
||||
value.as_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a $value> for $prim_int {
|
||||
fn from(value: &'a $value) -> Self {
|
||||
value.as_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl $value {
|
||||
pub fn as_int(&self) -> $prim_int {
|
||||
let mut le_bytes = (0 as $prim_int).to_le_bytes();
|
||||
let bitslice = BitSlice::<u8, Lsb0>::from_slice_mut(&mut le_bytes);
|
||||
bitslice.clone_from_bitslice(self.bits());
|
||||
$prim_int::from_le_bytes(le_bytes)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_for_all_prim_uints!(
|
||||
#[usize =, NonZero =]
|
||||
impl_prim_int_from_value!()
|
||||
);
|
||||
|
||||
impl_for_all_prim_sints!(
|
||||
#[isize =, NonZero =]
|
||||
impl_prim_int_from_value!()
|
||||
);
|
||||
|
||||
pub trait BoolOrIntType:
|
||||
Type<SimValue = <Self as BoolOrIntType>::Value> + sealed::BoolOrIntTypeSealed
|
||||
{
|
||||
type Width: Size;
|
||||
type Signed: GenericConstBool;
|
||||
type Value: Clone
|
||||
|
|
@ -1301,12 +1819,80 @@ impl StaticType for Bool {
|
|||
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
|
||||
}
|
||||
|
||||
impl From<bool> for UIntValue<ConstUsize<1>> {
|
||||
fn from(value: bool) -> Self {
|
||||
static VALUES: OnceLock<[UIntValue<ConstUsize<1>>; 2]> = OnceLock::new();
|
||||
let values = VALUES
|
||||
.get_or_init(|| std::array::from_fn(|i| UInt::<1>::new_static().from_int_wrapping(i)));
|
||||
values[value as usize].clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for SIntValue<ConstUsize<1>> {
|
||||
fn from(value: bool) -> Self {
|
||||
SIntValue::new(UIntValue::<ConstUsize<1>>::from(value).into_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for UIntValue {
|
||||
fn from(value: bool) -> Self {
|
||||
UIntValue::new(UIntValue::<ConstUsize<1>>::from(value).into_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for SIntValue {
|
||||
fn from(value: bool) -> Self {
|
||||
SIntValue::new(UIntValue::<ConstUsize<1>>::from(value).into_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToLiteralBits for bool {
|
||||
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
|
||||
Ok(interned_bit(*self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToSimValueInner<'a> for bool {
|
||||
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
|
||||
Cow::Owned(*this)
|
||||
}
|
||||
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
|
||||
Cow::Owned(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Not for Valueless<Bool> {
|
||||
type Output = Valueless<Bool>;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitAnd for Valueless<Bool> {
|
||||
type Output = Valueless<Bool>;
|
||||
|
||||
fn bitand(self, _rhs: Valueless<Bool>) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitOr for Valueless<Bool> {
|
||||
type Output = Valueless<Bool>;
|
||||
|
||||
fn bitor(self, _rhs: Valueless<Bool>) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitXor for Valueless<Bool> {
|
||||
type Output = Valueless<Bool>;
|
||||
|
||||
fn bitxor(self, _rhs: Valueless<Bool>) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
use crate::{
|
||||
bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder},
|
||||
expr::{
|
||||
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd,
|
||||
ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd},
|
||||
CastBitsTo, CastTo, CastToBits, CastToImpl, Expr, HdlPartialEq, HdlPartialEqImpl,
|
||||
HdlPartialOrd, HdlPartialOrdImpl,
|
||||
},
|
||||
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType},
|
||||
intern::{Intern, Interned},
|
||||
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType, UIntValue},
|
||||
intern::{Intern, InternSlice, Interned},
|
||||
phantom_const::PhantomConst,
|
||||
sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType},
|
||||
sim::value::{SimValue, ToSimValueWithType},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
|
||||
|
|
@ -22,7 +22,7 @@ use serde::{
|
|||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
de::{Error, Visitor, value::UsizeDeserializer},
|
||||
};
|
||||
use std::{fmt, marker::PhantomData, ops::Index};
|
||||
use std::{borrow::Cow, fmt, marker::PhantomData, ops::Index};
|
||||
|
||||
const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"];
|
||||
|
||||
|
|
@ -96,7 +96,6 @@ impl Type for UIntInRangeMaskType {
|
|||
|
||||
impl BundleType for UIntInRangeMaskType {
|
||||
type Builder = NoBuilder;
|
||||
type FilledBuilder = Expr<UIntInRangeMaskType>;
|
||||
|
||||
fn fields(&self) -> Interned<[BundleField]> {
|
||||
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
|
||||
|
|
@ -112,8 +111,8 @@ impl BundleType for UIntInRangeMaskType {
|
|||
flipped: false,
|
||||
ty: range.canonical(),
|
||||
},
|
||||
][..]
|
||||
.intern()
|
||||
]
|
||||
.intern_slice()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,30 +135,57 @@ impl ToSimValueWithType<UIntInRangeMaskType> for bool {
|
|||
}
|
||||
}
|
||||
|
||||
impl ExprCastTo<Bool> for UIntInRangeMaskType {
|
||||
fn cast_to(src: Expr<Self>, to_type: Bool) -> Expr<Bool> {
|
||||
src.cast_to_bits().cast_to(to_type)
|
||||
impl CastToImpl<Bool> for UIntInRangeMaskType {
|
||||
type ValueOutput = bool;
|
||||
|
||||
fn cast_value_to(
|
||||
_this: Self,
|
||||
value: Cow<'_, Self::SimValue>,
|
||||
_to_type: Bool,
|
||||
) -> Self::ValueOutput {
|
||||
*value
|
||||
}
|
||||
|
||||
fn cast_expr_to(value: Expr<Self>, to_type: Bool) -> Expr<Bool> {
|
||||
value.cast_to_bits().cast_to(to_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExprCastTo<UIntInRangeMaskType> for Bool {
|
||||
fn cast_to(src: Expr<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> {
|
||||
src.cast_to_static::<UInt<1>>().cast_bits_to(to_type)
|
||||
impl CastToImpl<UIntInRangeMaskType> for Bool {
|
||||
type ValueOutput = SimValue<UIntInRangeMaskType>;
|
||||
|
||||
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 ExprPartialEq<Self> for UIntInRangeMaskType {
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
impl HdlPartialEqImpl<Self> for UIntInRangeMaskType {
|
||||
#[track_caller]
|
||||
fn cmp_value_eq(
|
||||
_lhs: Self,
|
||||
lhs_value: Cow<'_, Self::SimValue>,
|
||||
_rhs: Self,
|
||||
rhs_value: Cow<'_, Self::SimValue>,
|
||||
) -> bool {
|
||||
*lhs_value == *rhs_value
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
|
||||
}
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
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
|
||||
#[track_caller]
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -300,9 +326,16 @@ macro_rules! define_uint_in_range_type {
|
|||
}
|
||||
}
|
||||
pub fn new(start: Start::SizeType, end: End::SizeType) -> Self {
|
||||
Self::from_phantom_const_range(PhantomConst::new(
|
||||
$SerdeRange { start, end }.intern_sized(),
|
||||
))
|
||||
Self::from_phantom_const_range(PhantomConst::new_sized($SerdeRange { start, end }))
|
||||
}
|
||||
pub fn bit_width(self) -> usize {
|
||||
self.value.width()
|
||||
}
|
||||
pub fn start(self) -> Start::SizeType {
|
||||
self.range.get().start
|
||||
}
|
||||
pub fn end(self) -> End::SizeType {
|
||||
self.range.get().end
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +426,6 @@ macro_rules! define_uint_in_range_type {
|
|||
|
||||
impl<Start: Size, End: Size> BundleType for $UIntInRangeType<Start, End> {
|
||||
type Builder = NoBuilder;
|
||||
type FilledBuilder = Expr<Self>;
|
||||
|
||||
fn fields(&self) -> Interned<[BundleField]> {
|
||||
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
|
||||
|
|
@ -409,8 +441,8 @@ macro_rules! define_uint_in_range_type {
|
|||
flipped: false,
|
||||
ty: range.canonical(),
|
||||
},
|
||||
][..]
|
||||
.intern()
|
||||
]
|
||||
.intern_slice()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -477,32 +509,64 @@ macro_rules! define_uint_in_range_type {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Start: Size, End: Size> ExprCastTo<UInt> for $UIntInRangeType<Start, End> {
|
||||
fn cast_to(src: Expr<Self>, to_type: UInt) -> Expr<UInt> {
|
||||
src.cast_to_bits().cast_to(to_type)
|
||||
impl<Start: Size, End: Size, Width: Size> CastToImpl<UIntType<Width>>
|
||||
for $UIntInRangeType<Start, End>
|
||||
{
|
||||
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> ExprCastTo<$UIntInRangeType<Start, End>> for UInt {
|
||||
fn cast_to(
|
||||
src: Expr<Self>,
|
||||
impl<Start: Size, End: Size, Width: Size> CastToImpl<$UIntInRangeType<Start, End>>
|
||||
for UIntType<Width>
|
||||
{
|
||||
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>,
|
||||
) -> Expr<$UIntInRangeType<Start, End>> {
|
||||
src.cast_bits_to(to_type)
|
||||
value.cast_to(to_type.value).cast_bits_to(to_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
|
||||
ExprPartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
|
||||
HdlPartialEqImpl<$UIntInRangeType<RhsStart, RhsEnd>>
|
||||
for $UIntInRangeType<LhsStart, LhsEnd>
|
||||
{
|
||||
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>,
|
||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||
) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
|
||||
}
|
||||
fn cmp_ne(
|
||||
fn cmp_expr_ne(
|
||||
lhs: Expr<Self>,
|
||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||
) -> Expr<Bool> {
|
||||
|
|
@ -511,28 +575,60 @@ macro_rules! define_uint_in_range_type {
|
|||
}
|
||||
|
||||
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
|
||||
ExprPartialOrd<$UIntInRangeType<RhsStart, RhsEnd>>
|
||||
HdlPartialOrdImpl<$UIntInRangeType<RhsStart, RhsEnd>>
|
||||
for $UIntInRangeType<LhsStart, LhsEnd>
|
||||
{
|
||||
fn cmp_lt(
|
||||
fn cmp_value_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>,
|
||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||
) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits())
|
||||
}
|
||||
fn cmp_le(
|
||||
fn cmp_expr_le(
|
||||
lhs: Expr<Self>,
|
||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||
) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_le(rhs.cast_to_bits())
|
||||
}
|
||||
fn cmp_gt(
|
||||
fn cmp_expr_gt(
|
||||
lhs: Expr<Self>,
|
||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||
) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits())
|
||||
}
|
||||
fn cmp_ge(
|
||||
fn cmp_expr_ge(
|
||||
lhs: Expr<Self>,
|
||||
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
|
||||
) -> Expr<Bool> {
|
||||
|
|
@ -540,70 +636,138 @@ macro_rules! define_uint_in_range_type {
|
|||
}
|
||||
}
|
||||
|
||||
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>>
|
||||
impl<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<UIntType<Width>>
|
||||
for $UIntInRangeType<Start, End>
|
||||
{
|
||||
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)
|
||||
}
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_ne(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Start: Size, End: Size, Width: Size> ExprPartialEq<$UIntInRangeType<Start, End>>
|
||||
impl<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<$UIntInRangeType<Start, End>>
|
||||
for UIntType<Width>
|
||||
{
|
||||
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())
|
||||
}
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
lhs.cmp_ne(rhs.cast_to_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<UIntType<Width>>
|
||||
impl<Start: Size, End: Size, Width: Size> HdlPartialOrdImpl<UIntType<Width>>
|
||||
for $UIntInRangeType<Start, End>
|
||||
{
|
||||
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
fn cmp_value_lt(
|
||||
_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)
|
||||
}
|
||||
fn cmp_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_le(rhs)
|
||||
}
|
||||
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_gt(rhs)
|
||||
}
|
||||
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
|
||||
lhs.cast_to_bits().cmp_ge(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<$UIntInRangeType<Start, End>>
|
||||
impl<Start: Size, End: Size, Width: Size> HdlPartialOrdImpl<$UIntInRangeType<Start, End>>
|
||||
for UIntType<Width>
|
||||
{
|
||||
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
fn cmp_value_lt(
|
||||
_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())
|
||||
}
|
||||
fn cmp_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
lhs.cmp_le(rhs.cast_to_bits())
|
||||
}
|
||||
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
lhs.cmp_gt(rhs.cast_to_bits())
|
||||
}
|
||||
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
|
||||
lhs.cmp_ge(rhs.cast_to_bits())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@ use std::{
|
|||
any::{Any, TypeId},
|
||||
borrow::{Borrow, Cow},
|
||||
cmp::Ordering,
|
||||
ffi::{OsStr, OsString},
|
||||
fmt,
|
||||
hash::{BuildHasher, Hash, Hasher},
|
||||
iter::FusedIterator,
|
||||
marker::PhantomData,
|
||||
ops::Deref,
|
||||
path::{Path, PathBuf},
|
||||
sync::{Mutex, RwLock},
|
||||
};
|
||||
|
||||
|
|
@ -287,15 +289,266 @@ impl InternedCompare for BitSlice {
|
|||
}
|
||||
}
|
||||
|
||||
impl InternedCompare for str {
|
||||
type InternedCompareKey = PtrEqWithMetadata<Self>;
|
||||
/// Safety: `as_bytes` and `from_bytes_unchecked` must return the same pointer as the input.
|
||||
/// all values returned by `as_bytes` must be valid to pass to `from_bytes_unchecked`.
|
||||
/// `into_bytes` must return the exact same thing as `as_bytes`.
|
||||
/// `Interned<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 {
|
||||
PtrEqWithMetadata(this)
|
||||
PtrEqWithMetadata(InternStrLike::as_bytes(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 {
|
||||
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>
|
||||
where
|
||||
Self: Clone,
|
||||
|
|
@ -316,6 +569,30 @@ pub trait Intern: Any + Send + Sync {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Intern + ToOwned> From<Cow<'_, T>> for Interned<T> {
|
||||
fn from(value: Cow<'_, T>) -> Self {
|
||||
Intern::intern_cow(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Intern> From<&'_ T> for Interned<T> {
|
||||
fn from(value: &'_ T) -> Self {
|
||||
Intern::intern(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Intern + Clone> From<T> for Interned<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Intern::intern_sized(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + 'static + Send + Sync + ToOwned> From<Interned<T>> for Cow<'_, T> {
|
||||
fn from(value: Interned<T>) -> Self {
|
||||
Cow::Borrowed(Interned::into_inner(value))
|
||||
}
|
||||
}
|
||||
|
||||
struct InternerState<T: ?Sized + 'static + Send + Sync> {
|
||||
table: HashTable<&'static T>,
|
||||
hasher: DefaultBuildHasher,
|
||||
|
|
@ -381,12 +658,6 @@ impl Interner<BitSlice> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Interner<str> {
|
||||
fn intern_str(&self, value: Cow<'_, str>) -> Interned<str> {
|
||||
self.intern(|value| value.into_owned().leak(), value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Interned<T: ?Sized + 'static + Send + Sync> {
|
||||
inner: &'static T,
|
||||
}
|
||||
|
|
@ -416,6 +687,12 @@ forward_fmt_trait!(Pointer);
|
|||
forward_fmt_trait!(UpperExp);
|
||||
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)]
|
||||
pub struct InternedSliceIter<T: Clone + 'static + Send + Sync> {
|
||||
slice: Interned<[T]>,
|
||||
|
|
@ -485,6 +762,57 @@ 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> {
|
||||
fn from(value: Interned<[T]>) -> Self {
|
||||
Vec::from(&*value)
|
||||
|
|
@ -497,24 +825,12 @@ 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]>
|
||||
where
|
||||
[I]: Intern,
|
||||
{
|
||||
fn default() -> Self {
|
||||
[][..].intern()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Interned<str> {
|
||||
fn default() -> Self {
|
||||
"".intern()
|
||||
Intern::intern(&[])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -645,15 +961,6 @@ 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 {
|
||||
fn intern(&self) -> Interned<Self> {
|
||||
Self::intern_cow(Cow::Borrowed(self))
|
||||
|
|
@ -714,26 +1021,6 @@ impl Intern for BitSlice {
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy {
|
||||
type InputRef<'a>: 'a + Send + Sync + Hash + Copy;
|
||||
type InputOwned: 'static + Send + Sync;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,18 @@
|
|||
// TODO: enable:
|
||||
// #![warn(missing_docs)]
|
||||
|
||||
#![deny(
|
||||
rustdoc::bare_urls,
|
||||
rustdoc::broken_intra_doc_links,
|
||||
rustdoc::invalid_codeblock_attributes,
|
||||
rustdoc::invalid_html_tags,
|
||||
rustdoc::invalid_rust_codeblocks,
|
||||
rustdoc::private_doc_tests,
|
||||
rustdoc::private_intra_doc_links,
|
||||
rustdoc::redundant_explicit_links,
|
||||
rustdoc::unescaped_backticks
|
||||
)]
|
||||
|
||||
//! [Main Documentation][_docs]
|
||||
|
||||
extern crate self as fayalite;
|
||||
|
|
@ -74,6 +86,135 @@ macro_rules! __cfg_expansion_helper {
|
|||
pub use fayalite_proc_macros::hdl_module;
|
||||
|
||||
#[doc(inline)]
|
||||
/// The `#[hdl]` attribute is supported on several different kinds of [Rust Items](https://doc.rust-lang.org/reference/items.html):
|
||||
///
|
||||
/// # Functions and Methods
|
||||
/// Enable's the stuff that you can use inside a [module's body](crate::_docs::modules::module_bodies),
|
||||
/// but without being a module or changing the function's signature.
|
||||
/// The only exception is that you can't use stuff that requires the automatically-provided `m` variable.
|
||||
///
|
||||
/// # Structs
|
||||
// TODO: expand on struct docs
|
||||
/// e.g.:
|
||||
/// ```
|
||||
/// # use fayalite::prelude::*;
|
||||
/// # #[hdl]
|
||||
/// # pub struct OtherStruct {}
|
||||
/// #[hdl]
|
||||
/// pub struct MyStruct {
|
||||
/// #[hdl(flip)]
|
||||
/// pub a: UInt<5>,
|
||||
/// pub b: Bool,
|
||||
/// #[hdl(flip)]
|
||||
/// pub c: OtherStruct,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Enums
|
||||
// TODO: expand on enum docs
|
||||
/// e.g.:
|
||||
/// ```
|
||||
/// # use fayalite::prelude::*;
|
||||
/// # #[hdl]
|
||||
/// # pub struct MyStruct {}
|
||||
/// #[hdl]
|
||||
/// pub enum MyEnum {
|
||||
/// A(UInt<3>),
|
||||
/// B,
|
||||
/// C(MyStruct),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Type Aliases
|
||||
///
|
||||
/// There's three different ways you can create a type alias:
|
||||
///
|
||||
/// # Normal Type Alias
|
||||
///
|
||||
/// This works exactly how you'd expect:
|
||||
/// ```
|
||||
/// # use fayalite::prelude::*;
|
||||
/// # #[hdl]
|
||||
/// # pub struct MyStruct<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 bitvec;
|
||||
|
|
@ -87,8 +228,8 @@ pub mod _docs;
|
|||
|
||||
pub mod annotations;
|
||||
pub mod array;
|
||||
pub mod build;
|
||||
pub mod bundle;
|
||||
pub mod cli;
|
||||
pub mod clock;
|
||||
pub mod enum_;
|
||||
pub mod expr;
|
||||
|
|
@ -99,6 +240,7 @@ pub mod intern;
|
|||
pub mod memory;
|
||||
pub mod module;
|
||||
pub mod phantom_const;
|
||||
pub mod platform;
|
||||
pub mod prelude;
|
||||
pub mod reg;
|
||||
pub mod reset;
|
||||
|
|
@ -107,4 +249,5 @@ pub mod source_location;
|
|||
pub mod testing;
|
||||
pub mod ty;
|
||||
pub mod util;
|
||||
pub mod vendor;
|
||||
pub mod wire;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ use crate::{
|
|||
array::{Array, ArrayType},
|
||||
bundle::{Bundle, BundleType},
|
||||
clock::Clock,
|
||||
expr::{Expr, Flow, ToExpr, ToLiteralBits, ops::BundleLiteral, repeat},
|
||||
expr::{
|
||||
Expr, Flow, ToExpr, ToLiteralBits, ValueType, ops::BundleLiteral, repeat,
|
||||
value_category::ValueCategoryExpr,
|
||||
},
|
||||
hdl,
|
||||
int::{Bool, DynSize, Size, UInt, UIntType},
|
||||
intern::{Intern, Interned},
|
||||
|
|
@ -366,10 +369,16 @@ impl<T: PortType> fmt::Debug for MemPort<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: PortType> MemPort<T> {
|
||||
pub fn ty(&self) -> T::Port {
|
||||
impl<T: PortType> ValueType for MemPort<T> {
|
||||
type Type = T::Port;
|
||||
type ValueCategory = ValueCategoryExpr;
|
||||
|
||||
fn ty(&self) -> T::Port {
|
||||
T::port_ty(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PortType> MemPort<T> {
|
||||
pub fn source_location(&self) -> SourceLocation {
|
||||
self.source_location
|
||||
}
|
||||
|
|
@ -830,7 +839,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
depth: Option<usize>,
|
||||
initial_value: Expr<Array>,
|
||||
) -> Interned<BitSlice> {
|
||||
let initial_value_ty = Expr::ty(initial_value);
|
||||
let initial_value_ty = initial_value.ty();
|
||||
assert_eq!(
|
||||
*mem_element_type,
|
||||
Element::from_canonical(initial_value_ty.element()),
|
||||
|
|
@ -1011,7 +1020,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
target.depth,
|
||||
initial_value,
|
||||
));
|
||||
target.depth = Some(Expr::ty(initial_value).len());
|
||||
target.depth = Some(initial_value.ty().len());
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {
|
||||
|
|
|
|||
|
|
@ -8,17 +8,19 @@ use crate::{
|
|||
clock::{Clock, ClockDomain},
|
||||
enum_::{Enum, EnumMatchVariantsIter, EnumType},
|
||||
expr::{
|
||||
Expr, Flow, ToExpr,
|
||||
Expr, Flow, ToExpr, ValueType,
|
||||
ops::VariantAccess,
|
||||
target::{
|
||||
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField,
|
||||
TargetPathElement,
|
||||
},
|
||||
value_category::ValueCategoryExpr,
|
||||
},
|
||||
formal::FormalKind,
|
||||
int::{Bool, DynSize, Size},
|
||||
intern::{Intern, Interned},
|
||||
memory::{Mem, MemBuilder, MemBuilderTarget, PortName},
|
||||
platform::PlatformIOBuilder,
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
|
||||
sim::{ExternModuleSimGenerator, ExternModuleSimulation},
|
||||
|
|
@ -40,7 +42,6 @@ use std::{
|
|||
marker::PhantomData,
|
||||
mem,
|
||||
num::NonZeroU64,
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
sync::atomic::AtomicU64,
|
||||
};
|
||||
|
|
@ -66,6 +67,8 @@ pub trait ModuleBuildingStatus:
|
|||
type ModuleBody: fmt::Debug;
|
||||
type StmtAnnotations: 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug;
|
||||
type ModuleIOAnnotations;
|
||||
type ExternModuleParameters: fmt::Debug;
|
||||
type ExternModuleClocksForPast: fmt::Debug;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
||||
|
|
@ -78,6 +81,8 @@ impl ModuleBuildingStatus for ModuleBuilt {
|
|||
type ModuleBody = Block;
|
||||
type StmtAnnotations = Interned<[TargetedAnnotation]>;
|
||||
type ModuleIOAnnotations = Interned<[TargetedAnnotation]>;
|
||||
type ExternModuleParameters = Interned<[ExternModuleParameter]>;
|
||||
type ExternModuleClocksForPast = Interned<[Target]>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
||||
|
|
@ -90,6 +95,8 @@ impl ModuleBuildingStatus for ModuleBuilding {
|
|||
type ModuleBody = BuilderModuleBody;
|
||||
type StmtAnnotations = ();
|
||||
type ModuleIOAnnotations = Vec<TargetedAnnotation>;
|
||||
type ExternModuleParameters = Vec<ExternModuleParameter>;
|
||||
type ExternModuleClocksForPast = Vec<Target>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -199,7 +206,7 @@ impl StmtConnect {
|
|||
source_location,
|
||||
} = *self;
|
||||
assert!(
|
||||
Expr::ty(lhs).can_connect(Expr::ty(rhs)),
|
||||
lhs.ty().can_connect(rhs.ty()),
|
||||
"can't connect types that are not equivalent:\nlhs type:\n{lhs_orig_ty:?}\nrhs type:\n{rhs_orig_ty:?}\nat: {source_location}",
|
||||
);
|
||||
assert!(
|
||||
|
|
@ -213,14 +220,14 @@ impl StmtConnect {
|
|||
match Expr::flow(rhs) {
|
||||
Flow::Source | Flow::Duplex => {}
|
||||
Flow::Sink => assert!(
|
||||
Expr::ty(rhs).is_passive(),
|
||||
rhs.ty().is_passive(),
|
||||
"can't connect from sink with non-passive type\nat: {source_location}"
|
||||
),
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
fn assert_validity(&self) {
|
||||
self.assert_validity_with_original_types(Expr::ty(self.lhs), Expr::ty(self.rhs));
|
||||
self.assert_validity_with_original_types(self.lhs.ty(), self.rhs.ty());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,7 +332,7 @@ impl Copy for StmtMatch {}
|
|||
impl StmtMatch {
|
||||
#[track_caller]
|
||||
fn assert_validity(&self) {
|
||||
assert_eq!(Expr::ty(self.expr).variants().len(), self.blocks.len());
|
||||
assert_eq!(self.expr.ty().variants().len(), self.blocks.len());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -759,6 +766,15 @@ impl<T: BundleType> fmt::Debug for Instance<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: BundleType> ValueType for Instance<T> {
|
||||
type Type = T;
|
||||
type ValueCategory = ValueCategoryExpr;
|
||||
|
||||
fn ty(&self) -> T {
|
||||
self.instantiated.io_ty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BundleType> Instance<T> {
|
||||
pub fn canonical(self) -> Instance<Bundle> {
|
||||
let Self {
|
||||
|
|
@ -822,9 +838,6 @@ impl<T: BundleType> Instance<T> {
|
|||
pub fn must_connect_to(&self) -> bool {
|
||||
true
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.instantiated.io_ty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
|
@ -833,6 +846,8 @@ pub struct AnnotatedModuleIO<S: ModuleBuildingStatus = ModuleBuilt> {
|
|||
pub module_io: ModuleIO<CanonicalType>,
|
||||
}
|
||||
|
||||
impl Copy for AnnotatedModuleIO {}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum ModuleKind {
|
||||
Extern,
|
||||
|
|
@ -1077,26 +1092,65 @@ impl From<NormalModuleBody> for ModuleBody {
|
|||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn validate_clock_for_past<S: ModuleBuildingStatus>(
|
||||
clock_for_past: Option<Target>,
|
||||
module_io: &[AnnotatedModuleIO<S>],
|
||||
) -> Target {
|
||||
if let Some(clock_for_past) = clock_for_past {
|
||||
assert_eq!(
|
||||
clock_for_past.canonical_ty(),
|
||||
Clock.canonical(),
|
||||
"clock_for_past: clock is not of type Clock",
|
||||
);
|
||||
if clock_for_past
|
||||
.base()
|
||||
.module_io()
|
||||
.is_some_and(|v| module_io.iter().any(|module_io| module_io.module_io == *v))
|
||||
{
|
||||
let mut target = clock_for_past;
|
||||
while let Target::Child(child) = target {
|
||||
match *child.path_element() {
|
||||
TargetPathElement::BundleField(_) | TargetPathElement::ArrayElement(_) => {}
|
||||
TargetPathElement::DynArrayElement(_) => {
|
||||
panic!(
|
||||
"clock_for_past: clock must be a static target (you can't use `Expr<UInt>` array indexes):\n{clock_for_past:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
target = *child.parent();
|
||||
}
|
||||
return clock_for_past;
|
||||
}
|
||||
}
|
||||
panic!("clock_for_past: clock must be some part of this module's I/O:\n{clock_for_past:?}");
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
pub struct ExternModuleBody<
|
||||
P: Deref<Target = [ExternModuleParameter]> = Interned<[ExternModuleParameter]>,
|
||||
> {
|
||||
pub struct ExternModuleBody<S: ModuleBuildingStatus = ModuleBuilt> {
|
||||
pub verilog_name: Interned<str>,
|
||||
pub parameters: P,
|
||||
pub parameters: S::ExternModuleParameters,
|
||||
/// [`Clock`]s that the [`Simulation`] will store the past values of all [`ModuleIO`] for.
|
||||
///
|
||||
/// [`Simulation`]: crate::sim::Simulation
|
||||
pub clocks_for_past: S::ExternModuleClocksForPast,
|
||||
pub simulation: Option<ExternModuleSimulation>,
|
||||
}
|
||||
|
||||
impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody {
|
||||
fn from(value: ExternModuleBody<Vec<ExternModuleParameter>>) -> Self {
|
||||
impl From<ExternModuleBody<ModuleBuilding>> for ExternModuleBody {
|
||||
fn from(value: ExternModuleBody<ModuleBuilding>) -> Self {
|
||||
let ExternModuleBody {
|
||||
verilog_name,
|
||||
parameters,
|
||||
clocks_for_past,
|
||||
simulation,
|
||||
} = value;
|
||||
let parameters = Intern::intern_owned(parameters);
|
||||
let clocks_for_past = Intern::intern_owned(clocks_for_past);
|
||||
Self {
|
||||
verilog_name,
|
||||
parameters,
|
||||
clocks_for_past,
|
||||
simulation,
|
||||
}
|
||||
}
|
||||
|
|
@ -1109,15 +1163,12 @@ impl From<ExternModuleBody> for ModuleBody {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ModuleBody<
|
||||
S: ModuleBuildingStatus = ModuleBuilt,
|
||||
P: Deref<Target = [ExternModuleParameter]> = Interned<[ExternModuleParameter]>,
|
||||
> {
|
||||
pub enum ModuleBody<S: ModuleBuildingStatus = ModuleBuilt> {
|
||||
Normal(NormalModuleBody<S>),
|
||||
Extern(ExternModuleBody<P>),
|
||||
Extern(ExternModuleBody<S>),
|
||||
}
|
||||
|
||||
pub(crate) type ModuleBodyBuilding = ModuleBody<ModuleBuilding, Vec<ExternModuleParameter>>;
|
||||
pub(crate) type ModuleBodyBuilding = ModuleBody<ModuleBuilding>;
|
||||
|
||||
impl ModuleBodyBuilding {
|
||||
pub(crate) fn builder_normal_body_opt(
|
||||
|
|
@ -1138,9 +1189,7 @@ impl ModuleBodyBuilding {
|
|||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub(crate) fn builder_extern_body(
|
||||
&mut self,
|
||||
) -> &mut ExternModuleBody<Vec<ExternModuleParameter>> {
|
||||
pub(crate) fn builder_extern_body(&mut self) -> &mut ExternModuleBody<ModuleBuilding> {
|
||||
if let Self::Extern(v) = self {
|
||||
v
|
||||
} else {
|
||||
|
|
@ -1212,6 +1261,12 @@ pub struct Module<T: BundleType> {
|
|||
module_annotations: Interned<[Annotation]>,
|
||||
}
|
||||
|
||||
impl<T: BundleType> AsRef<Self> for Module<T> {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DebugFmtModulesState {
|
||||
seen: HashSet<NameId>,
|
||||
|
|
@ -1288,11 +1343,13 @@ impl<T: BundleType> fmt::Debug for DebugModuleBody<T> {
|
|||
ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name,
|
||||
parameters,
|
||||
clocks_for_past,
|
||||
simulation,
|
||||
}) => {
|
||||
debug_struct
|
||||
.field("verilog_name", verilog_name)
|
||||
.field("parameters", parameters)
|
||||
.field("clocks_for_past", clocks_for_past)
|
||||
.field("simulation", simulation);
|
||||
}
|
||||
}
|
||||
|
|
@ -1771,8 +1828,13 @@ impl AssertValidityState {
|
|||
ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name: _,
|
||||
parameters: _,
|
||||
clocks_for_past,
|
||||
simulation: _,
|
||||
}) => {}
|
||||
}) => {
|
||||
for clock_for_past in clocks_for_past {
|
||||
validate_clock_for_past(Some(clock_for_past), &self.module.module_io);
|
||||
}
|
||||
}
|
||||
ModuleBody::Normal(NormalModuleBody { body }) => {
|
||||
let body = self.make_block_index(body);
|
||||
assert_eq!(body, 0);
|
||||
|
|
@ -1802,9 +1864,17 @@ impl<T: BundleType> Module<T> {
|
|||
match &mut body {
|
||||
ModuleBody::Normal(_) => {}
|
||||
ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name: _,
|
||||
parameters: _,
|
||||
clocks_for_past,
|
||||
simulation: Some(simulation),
|
||||
..
|
||||
}) => {
|
||||
let mut clocks_for_past_set = HashSet::default();
|
||||
*clocks_for_past = clocks_for_past
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|clock_for_past| clocks_for_past_set.insert(*clock_for_past))
|
||||
.collect();
|
||||
if module_io.iter().any(|io| {
|
||||
!simulation
|
||||
.sim_io_to_generator_map
|
||||
|
|
@ -1965,7 +2035,7 @@ impl<CD> RegBuilder<CD, (), ()> {
|
|||
init: _,
|
||||
ty: _,
|
||||
} = self;
|
||||
let ty = Expr::ty(init);
|
||||
let ty = init.ty();
|
||||
RegBuilder {
|
||||
name,
|
||||
source_location,
|
||||
|
|
@ -2111,6 +2181,27 @@ impl ModuleBuilder {
|
|||
self.output_with_loc(implicit_name.0, SourceLocation::caller(), ty)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn add_platform_io_with_loc(
|
||||
&self,
|
||||
name: &str,
|
||||
source_location: SourceLocation,
|
||||
platform_io_builder: PlatformIOBuilder<'_>,
|
||||
) -> Expr<Bundle> {
|
||||
platform_io_builder.add_platform_io(name, source_location, self)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn add_platform_io(
|
||||
&self,
|
||||
implicit_name: ImplicitName<'_>,
|
||||
platform_io_builder: PlatformIOBuilder<'_>,
|
||||
) -> Expr<Bundle> {
|
||||
self.add_platform_io_with_loc(
|
||||
implicit_name.0,
|
||||
SourceLocation::caller(),
|
||||
platform_io_builder,
|
||||
)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn run<T: BundleType>(
|
||||
name: &str,
|
||||
module_kind: ModuleKind,
|
||||
|
|
@ -2156,6 +2247,7 @@ impl ModuleBuilder {
|
|||
ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name: name.0,
|
||||
parameters: vec![],
|
||||
clocks_for_past: vec![],
|
||||
simulation: None,
|
||||
}),
|
||||
ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody {
|
||||
|
|
@ -2278,6 +2370,20 @@ impl ModuleBuilder {
|
|||
value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()),
|
||||
});
|
||||
}
|
||||
/// registers a [`Clock`] so you can use it with the [`ExternModuleSimulationState::read_past()`] family of functions.
|
||||
///
|
||||
/// [`ExternModuleSimulationState::read_past()`]: crate::sim::ExternModuleSimulationState::read_past()
|
||||
#[track_caller]
|
||||
pub fn register_clock_for_past(&self, clock_for_past: impl ToExpr<Type = Clock>) {
|
||||
let clock_for_past = clock_for_past.to_expr().target().as_deref().copied();
|
||||
let mut impl_ = self.impl_.borrow_mut();
|
||||
let clock_for_past = validate_clock_for_past(clock_for_past, &impl_.io);
|
||||
impl_
|
||||
.body
|
||||
.builder_extern_body()
|
||||
.clocks_for_past
|
||||
.push(clock_for_past);
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn extern_module_simulation<G: ExternModuleSimGenerator>(&self, generator: G) {
|
||||
let mut impl_ = self.impl_.borrow_mut();
|
||||
|
|
@ -2498,7 +2604,7 @@ pub fn enum_match_variants_helper<T: EnumType>(
|
|||
ModuleBuilder::with(|m| {
|
||||
let mut impl_ = m.impl_.borrow_mut();
|
||||
let body = impl_.body.builder_normal_body();
|
||||
let enum_ty = Expr::ty(base);
|
||||
let enum_ty = base.ty();
|
||||
let outer_block: BlockId = m.block_stack.top();
|
||||
let blocks = Interned::from_iter(enum_ty.variants().iter().map(|_| body.new_block()));
|
||||
body.block(outer_block).stmts.push(
|
||||
|
|
@ -2550,7 +2656,7 @@ pub fn connect_any_with_loc<Lhs: ToExpr, Rhs: ToExpr>(
|
|||
rhs,
|
||||
source_location,
|
||||
};
|
||||
connect.assert_validity_with_original_types(Expr::ty(lhs_orig), Expr::ty(rhs_orig));
|
||||
connect.assert_validity_with_original_types(lhs_orig.ty(), rhs_orig.ty());
|
||||
ModuleBuilder::with(|m| {
|
||||
m.impl_
|
||||
.borrow_mut()
|
||||
|
|
@ -2665,7 +2771,7 @@ pub fn memory_with_init_and_loc<Element: Type, Len: Size>(
|
|||
source_location: SourceLocation,
|
||||
) -> MemBuilder<Element, Len> {
|
||||
let initial_value = initial_value.to_expr();
|
||||
let mut retval = memory_array_with_loc(name, Expr::ty(initial_value), source_location);
|
||||
let mut retval = memory_array_with_loc(name, initial_value.ty(), source_location);
|
||||
retval.initial_value(initial_value);
|
||||
retval
|
||||
}
|
||||
|
|
@ -2708,6 +2814,15 @@ pub struct ModuleIO<T: Type> {
|
|||
source_location: SourceLocation,
|
||||
}
|
||||
|
||||
impl<T: Type> ValueType for ModuleIO<T> {
|
||||
type Type = T;
|
||||
type ValueCategory = ValueCategoryExpr;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.ty
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> fmt::Debug for ModuleIO<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ModuleIO")
|
||||
|
|
@ -2735,6 +2850,22 @@ impl<T: Type> ModuleIO<T> {
|
|||
source_location,
|
||||
}
|
||||
}
|
||||
pub fn from_canonical(canonical_module_io: ModuleIO<CanonicalType>) -> Self {
|
||||
let ModuleIO {
|
||||
containing_module_name,
|
||||
bundle_field,
|
||||
id,
|
||||
ty,
|
||||
source_location,
|
||||
} = canonical_module_io;
|
||||
Self {
|
||||
containing_module_name,
|
||||
bundle_field,
|
||||
id,
|
||||
ty: T::from_canonical(ty),
|
||||
source_location,
|
||||
}
|
||||
}
|
||||
pub fn bundle_field(&self) -> BundleField {
|
||||
self.bundle_field
|
||||
}
|
||||
|
|
@ -2793,9 +2924,6 @@ impl<T: Type> ModuleIO<T> {
|
|||
unreachable!()
|
||||
}
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
bundle::{BundleField, BundleType},
|
||||
enum_::{EnumType, EnumVariant},
|
||||
expr::{
|
||||
ExprEnum,
|
||||
ExprEnum, ValueType,
|
||||
ops::{self, ArrayLiteral},
|
||||
target::{
|
||||
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
|
||||
|
|
@ -521,7 +521,7 @@ impl State {
|
|||
Entry::Vacant(entry) => (
|
||||
*entry.insert(Resets::with_new_nodes(
|
||||
&mut self.reset_graph,
|
||||
Expr::ty(expr),
|
||||
expr.ty(),
|
||||
source_location,
|
||||
)),
|
||||
true,
|
||||
|
|
@ -1020,7 +1020,7 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
|
|||
}
|
||||
macro_rules! match_arg_ty {
|
||||
($($Variant:ident),*) => {
|
||||
*match Expr::ty(arg) {
|
||||
*match arg.ty() {
|
||||
CanonicalType::Array(_)
|
||||
| CanonicalType::Enum(_)
|
||||
| CanonicalType::Bundle(_)
|
||||
|
|
@ -1660,7 +1660,8 @@ impl RunPassDispatch for AnyReg {
|
|||
let clock_domain = Expr::<Bundle>::from_canonical(
|
||||
Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0,
|
||||
);
|
||||
match Expr::ty(clock_domain)
|
||||
match clock_domain
|
||||
.ty()
|
||||
.field_by_name("rst".intern())
|
||||
.expect("ClockDomain has rst field")
|
||||
.ty
|
||||
|
|
@ -1802,6 +1803,7 @@ impl_run_pass_clone!([] ExternModuleParameter);
|
|||
impl_run_pass_clone!([] SIntValue);
|
||||
impl_run_pass_clone!([] std::ops::Range<usize>);
|
||||
impl_run_pass_clone!([] UIntValue);
|
||||
impl_run_pass_clone!([] crate::vendor::xilinx::XilinxAnnotation);
|
||||
impl_run_pass_copy!([] BlackBoxInlineAnnotation);
|
||||
impl_run_pass_copy!([] BlackBoxPathAnnotation);
|
||||
impl_run_pass_copy!([] bool);
|
||||
|
|
@ -2072,6 +2074,7 @@ impl_run_pass_for_struct! {
|
|||
impl[] RunPass for ExternModuleBody {
|
||||
verilog_name: _,
|
||||
parameters: _,
|
||||
clocks_for_past: _,
|
||||
simulation: _,
|
||||
}
|
||||
}
|
||||
|
|
@ -2217,6 +2220,7 @@ impl_run_pass_for_enum! {
|
|||
BlackBoxPath(v),
|
||||
DocString(v),
|
||||
CustomFirrtl(v),
|
||||
Xilinx(v),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ use crate::{
|
|||
bundle::{Bundle, BundleField, BundleType},
|
||||
enum_::{Enum, EnumType, EnumVariant},
|
||||
expr::{
|
||||
CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr,
|
||||
CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, ValueType,
|
||||
ops::{self, EnumLiteral},
|
||||
},
|
||||
hdl,
|
||||
int::UInt,
|
||||
intern::{Intern, Interned, Memoize},
|
||||
intern::{Intern, InternSlice, Interned, Memoize},
|
||||
memory::{DynPortType, Mem, MemPort},
|
||||
module::{
|
||||
Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
|
||||
|
|
@ -22,6 +22,7 @@ use crate::{
|
|||
wire::Wire,
|
||||
};
|
||||
use core::fmt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SimplifyEnumsError {
|
||||
|
|
@ -527,7 +528,7 @@ fn connect_port(
|
|||
rhs: Expr<CanonicalType>,
|
||||
source_location: SourceLocation,
|
||||
) {
|
||||
if Expr::ty(lhs) == Expr::ty(rhs) {
|
||||
if lhs.ty() == rhs.ty() {
|
||||
stmts.push(
|
||||
StmtConnect {
|
||||
lhs,
|
||||
|
|
@ -538,7 +539,7 @@ fn connect_port(
|
|||
);
|
||||
return;
|
||||
}
|
||||
match (Expr::ty(lhs), Expr::ty(rhs)) {
|
||||
match (lhs.ty(), rhs.ty()) {
|
||||
(CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => {
|
||||
let lhs = Expr::<Bundle>::from_canonical(lhs);
|
||||
for field in lhs_type.fields() {
|
||||
|
|
@ -585,8 +586,8 @@ fn connect_port(
|
|||
| (CanonicalType::PhantomConst(_), _)
|
||||
| (CanonicalType::DynSimOnly(_), _) => unreachable!(
|
||||
"trying to connect memory ports:\n{:?}\n{:?}",
|
||||
Expr::ty(lhs),
|
||||
Expr::ty(rhs),
|
||||
lhs.ty(),
|
||||
rhs.ty(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -606,20 +607,23 @@ fn match_int_tag(
|
|||
};
|
||||
};
|
||||
let mut retval = StmtIf {
|
||||
cond: int_tag_expr
|
||||
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)),
|
||||
cond: int_tag_expr.cmp_eq(
|
||||
int_tag_expr
|
||||
.ty()
|
||||
.from_int_wrapping(next_to_last_variant_index),
|
||||
),
|
||||
source_location,
|
||||
blocks: [next_to_last_block, last_block],
|
||||
};
|
||||
for (variant_index, block) in blocks_iter.rev() {
|
||||
retval = StmtIf {
|
||||
cond: int_tag_expr.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)),
|
||||
cond: int_tag_expr.cmp_eq(int_tag_expr.ty().from_int_wrapping(variant_index)),
|
||||
source_location,
|
||||
blocks: [
|
||||
block,
|
||||
Block {
|
||||
memories: Default::default(),
|
||||
stmts: [Stmt::from(retval)][..].intern(),
|
||||
stmts: [Stmt::from(retval)].intern_slice(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -656,7 +660,7 @@ impl Folder for State {
|
|||
ExprEnum::VariantAccess(op) => {
|
||||
let folded_base_expr = Expr::canonical(op.base()).fold(self)?;
|
||||
Ok(*Expr::expr_enum(self.handle_variant_access(
|
||||
Expr::ty(op.base()),
|
||||
op.base().ty(),
|
||||
folded_base_expr,
|
||||
op.variant_index(),
|
||||
)?))
|
||||
|
|
@ -866,7 +870,7 @@ impl Folder for State {
|
|||
}) => {
|
||||
let folded_expr = Expr::canonical(expr).fold(self)?;
|
||||
let folded_blocks = blocks.fold(self)?;
|
||||
self.handle_match(Expr::ty(expr), folded_expr, source_location, &folded_blocks)
|
||||
self.handle_match(expr.ty(), folded_expr, source_location, &folded_blocks)
|
||||
}
|
||||
Stmt::Connect(StmtConnect {
|
||||
lhs,
|
||||
|
|
@ -877,8 +881,8 @@ impl Folder for State {
|
|||
let folded_rhs = rhs.fold(self)?;
|
||||
let mut output_stmts = vec![];
|
||||
self.handle_stmt_connect(
|
||||
Expr::ty(lhs),
|
||||
Expr::ty(rhs),
|
||||
lhs.ty(),
|
||||
rhs.ty(),
|
||||
folded_lhs,
|
||||
folded_rhs,
|
||||
source_location,
|
||||
|
|
@ -955,12 +959,15 @@ impl Folder for State {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum SimplifyEnumsKind {
|
||||
SimplifyToEnumsWithNoBody,
|
||||
#[clap(name = "replace-with-bundle-of-uints")]
|
||||
#[serde(rename = "replace-with-bundle-of-uints")]
|
||||
ReplaceWithBundleOfUInts,
|
||||
#[clap(name = "replace-with-uint")]
|
||||
#[serde(rename = "replace-with-uint")]
|
||||
ReplaceWithUInt,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
annotations::TargetedAnnotation,
|
||||
array::Array,
|
||||
bundle::{Bundle, BundleType},
|
||||
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr},
|
||||
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr, ValueType},
|
||||
int::{Bool, SInt, Size, UInt},
|
||||
intern::{Intern, Interned},
|
||||
memory::{Mem, MemPort, PortType},
|
||||
|
|
@ -530,7 +530,7 @@ impl ModuleState {
|
|||
connect_read(
|
||||
output_stmts,
|
||||
wire_read,
|
||||
Expr::<UInt>::from_canonical(port_read).cast_bits_to(Expr::ty(wire_read)),
|
||||
Expr::<UInt>::from_canonical(port_read).cast_bits_to(wire_read.ty()),
|
||||
);
|
||||
};
|
||||
let connect_write_enum =
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
clock::Clock,
|
||||
enum_::{Enum, EnumType, EnumVariant},
|
||||
expr::{
|
||||
Expr, ExprEnum, ops,
|
||||
Expr, ExprEnum, ValueType, ops,
|
||||
target::{
|
||||
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
|
||||
TargetPathDynArrayElement, TargetPathElement,
|
||||
|
|
@ -33,6 +33,9 @@ use crate::{
|
|||
sim::{ExternModuleSimulation, value::DynSimOnly},
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
vendor::xilinx::{
|
||||
XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation,
|
||||
},
|
||||
wire::Wire,
|
||||
};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{
|
||||
Expr, ToExpr,
|
||||
ops::{ExprPartialEq, ExprPartialOrd},
|
||||
},
|
||||
expr::{Expr, HdlPartialEqImpl, HdlPartialOrdImpl, ToExpr, ValueType},
|
||||
int::Bool,
|
||||
intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize},
|
||||
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
|
||||
sim::value::{SimValue, ToSimValue, ToSimValueWithType},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
|
||||
|
|
@ -22,6 +19,7 @@ use serde::{
|
|||
};
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
|
|
@ -131,7 +129,7 @@ impl<T: Type + PhantomConstValue> Index<T> for PhantomConstWithoutGenerics {
|
|||
type Output = PhantomConst<T>;
|
||||
|
||||
fn index(&self, value: T) -> &Self::Output {
|
||||
Interned::into_inner(PhantomConst::new(value.intern()).intern_sized())
|
||||
Interned::into_inner(PhantomConst::new(&value).intern_sized())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -222,11 +220,26 @@ impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T,
|
|||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
||||
pub fn new(value: Interned<T>) -> Self {
|
||||
pub fn new_interned(value: Interned<T>) -> Self {
|
||||
Self {
|
||||
value: LazyInterned::Interned(value),
|
||||
}
|
||||
}
|
||||
pub fn new_sized(value: T) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
Self::new_interned(value.intern_sized())
|
||||
}
|
||||
pub fn new(value: &T) -> Self {
|
||||
Self::new_interned(value.intern())
|
||||
}
|
||||
pub fn new_deref<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_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
|
||||
Self {
|
||||
value: LazyInterned::new_lazy(v),
|
||||
|
|
@ -245,7 +258,7 @@ impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
|||
if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) {
|
||||
return retval;
|
||||
}
|
||||
<PhantomConst>::new(
|
||||
<PhantomConst>::new_interned(
|
||||
PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()),
|
||||
)
|
||||
}
|
||||
|
|
@ -253,7 +266,7 @@ impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
|||
if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) {
|
||||
return retval;
|
||||
}
|
||||
Self::new(
|
||||
Self::new_interned(
|
||||
PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()),
|
||||
)
|
||||
}
|
||||
|
|
@ -346,7 +359,9 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
|
|||
D: Deserializer<'de>,
|
||||
{
|
||||
match SerdeType::<T>::deserialize(deserializer)? {
|
||||
SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => Ok(Self::new(value)),
|
||||
SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => {
|
||||
Ok(Self::new_interned(value))
|
||||
}
|
||||
ty => Err(Error::invalid_value(
|
||||
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
|
||||
&"a PhantomConst",
|
||||
|
|
@ -355,50 +370,102 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> ExprPartialEq<Self> for PhantomConst<T> {
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
true.to_expr()
|
||||
}
|
||||
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
false.to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> ExprPartialOrd<Self> for PhantomConst<T> {
|
||||
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
false.to_expr()
|
||||
}
|
||||
|
||||
fn cmp_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
true.to_expr()
|
||||
}
|
||||
|
||||
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
false.to_expr()
|
||||
}
|
||||
|
||||
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
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));
|
||||
impl<T: ?Sized + PhantomConstValue> HdlPartialEqImpl<Self> for PhantomConst<T> {
|
||||
#[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()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(lhs.ty(), rhs.ty());
|
||||
false.to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> HdlPartialOrdImpl<Self> for PhantomConst<T> {
|
||||
#[track_caller]
|
||||
fn cmp_value_lt(
|
||||
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()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(lhs.ty(), rhs.ty());
|
||||
true.to_expr()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(lhs.ty(), rhs.ty());
|
||||
false.to_expr()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(lhs.ty(), rhs.ty());
|
||||
true.to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> ToSimValue for PhantomConst<T> {
|
||||
type Type = PhantomConst<T>;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_value(*self, *self)
|
||||
}
|
||||
|
|
@ -415,3 +482,71 @@ impl<T: ?Sized + PhantomConstValue> ToSimValueWithType<CanonicalType> for Phanto
|
|||
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())
|
||||
}
|
||||
|
|
|
|||
1923
crates/fayalite/src/platform.rs
Normal file
1923
crates/fayalite/src/platform.rs
Normal file
File diff suppressed because it is too large
Load diff
59
crates/fayalite/src/platform/peripherals.rs
Normal file
59
crates/fayalite/src/platform/peripherals.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::prelude::*;
|
||||
use ordered_float::NotNan;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub struct ClockInputProperties {
|
||||
pub frequency: NotNan<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,13 +7,13 @@ pub use crate::{
|
|||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
|
||||
},
|
||||
array::{Array, ArrayType},
|
||||
build::{BuildCli, JobParams, RunBuild},
|
||||
bundle::Bundle,
|
||||
cli::Cli,
|
||||
clock::{Clock, ClockDomain, ToClock},
|
||||
enum_::{Enum, HdlNone, HdlOption, HdlSome},
|
||||
expr::{
|
||||
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr,
|
||||
ReduceBits, ToExpr, repeat,
|
||||
ReduceBits, ToExpr, ValueType, repeat,
|
||||
},
|
||||
formal::{
|
||||
MakeFormalExpr, all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset,
|
||||
|
|
@ -27,7 +27,8 @@ pub use crate::{
|
|||
Instance, Module, ModuleBuilder, annotate, connect, connect_any, incomplete_wire, instance,
|
||||
memory, memory_array, memory_with_init, reg_builder, wire,
|
||||
},
|
||||
phantom_const::PhantomConst,
|
||||
phantom_const::{PhantomConst, PhantomConstGet},
|
||||
platform::{DynPlatform, Platform, PlatformIOBuilder, peripherals},
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
||||
sim::{
|
||||
|
|
@ -36,6 +37,7 @@ pub use crate::{
|
|||
value::{SimOnly, SimOnlyValue, SimValue, ToSimValue, ToSimValueWithType},
|
||||
},
|
||||
source_location::SourceLocation,
|
||||
testing::{FormalMode, assert_formal},
|
||||
ty::{AsMask, CanonicalType, Type},
|
||||
util::{ConstUsize, GenericConstUsize},
|
||||
wire::Wire,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
clock::ClockDomain,
|
||||
expr::{Expr, Flow},
|
||||
expr::{Expr, Flow, ValueType, value_category::ValueCategoryExpr},
|
||||
intern::Interned,
|
||||
module::{NameId, ScopedNameId},
|
||||
reset::{Reset, ResetType},
|
||||
|
|
@ -20,7 +20,16 @@ pub struct Reg<T: Type, R: ResetType = Reset> {
|
|||
init: Option<Expr<T>>,
|
||||
}
|
||||
|
||||
impl<T: Type + fmt::Debug, R: ResetType> fmt::Debug for Reg<T, R> {
|
||||
impl<T: Type, R: ResetType> ValueType 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 {
|
||||
let Self {
|
||||
name,
|
||||
|
|
@ -68,7 +77,7 @@ impl<T: Type, R: ResetType> Reg<T, R> {
|
|||
"register type must be a storable type"
|
||||
);
|
||||
if let Some(init) = init {
|
||||
assert_eq!(ty, Expr::ty(init), "register's type must match init type");
|
||||
assert_eq!(ty, init.ty(), "register's type must match init type");
|
||||
}
|
||||
Self {
|
||||
name: scoped_name,
|
||||
|
|
@ -78,9 +87,6 @@ impl<T: Type, R: ResetType> Reg<T, R> {
|
|||
init,
|
||||
}
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
}
|
||||
pub fn source_location(&self) -> SourceLocation {
|
||||
self.source_location
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
clock::Clock,
|
||||
expr::{Expr, ToExpr, ops},
|
||||
int::{Bool, SInt, UInt},
|
||||
expr::{CastToImpl, Expr, ValueType},
|
||||
int::{Bool, SInt, SIntValue, UInt, UIntValue},
|
||||
sim::value::SimValue,
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
|
||||
OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
|
||||
},
|
||||
util::ConstUsize,
|
||||
};
|
||||
use bitvec::{bits, order::Lsb0};
|
||||
|
||||
|
|
@ -19,15 +21,15 @@ mod sealed {
|
|||
pub trait ResetType:
|
||||
StaticType<MaskType = Bool>
|
||||
+ sealed::ResetTypeSealed
|
||||
+ ops::ExprCastTo<Bool>
|
||||
+ ops::ExprCastTo<Reset>
|
||||
+ ops::ExprCastTo<SyncReset>
|
||||
+ ops::ExprCastTo<AsyncReset>
|
||||
+ ops::ExprCastTo<Clock>
|
||||
+ ops::ExprCastTo<UInt<1>>
|
||||
+ ops::ExprCastTo<SInt<1>>
|
||||
+ ops::ExprCastTo<UInt>
|
||||
+ ops::ExprCastTo<SInt>
|
||||
+ CastToImpl<Bool, ValueOutput = bool>
|
||||
+ CastToImpl<Reset, ValueOutput = SimValue<Reset>>
|
||||
+ CastToImpl<SyncReset, ValueOutput = SimValue<SyncReset>>
|
||||
+ CastToImpl<AsyncReset, ValueOutput = SimValue<AsyncReset>>
|
||||
+ CastToImpl<Clock, ValueOutput = SimValue<Clock>>
|
||||
+ CastToImpl<UInt<1>, ValueOutput = UIntValue<ConstUsize<1>>>
|
||||
+ CastToImpl<SInt<1>, ValueOutput = SIntValue<ConstUsize<1>>>
|
||||
+ CastToImpl<UInt, ValueOutput = UIntValue>
|
||||
+ CastToImpl<SInt, ValueOutput = SIntValue>
|
||||
{
|
||||
fn dispatch<D: ResetTypeDispatch>(input: D::Input<Self>, dispatch: D) -> D::Output<Self>;
|
||||
}
|
||||
|
|
@ -132,29 +134,34 @@ macro_rules! reset_type {
|
|||
}
|
||||
|
||||
pub trait $Trait {
|
||||
fn $trait_fn(&self) -> Expr<$name>;
|
||||
type Output: ValueType<Type = $name>;
|
||||
fn $trait_fn(&self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for &'_ T {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
type Output = T::Output;
|
||||
fn $trait_fn(&self) -> Self::Output {
|
||||
(**self).$trait_fn()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
type Output = T::Output;
|
||||
fn $trait_fn(&self) -> Self::Output {
|
||||
(**self).$trait_fn()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for Box<T> {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
type Output = T::Output;
|
||||
fn $trait_fn(&self) -> Self::Output {
|
||||
(**self).$trait_fn()
|
||||
}
|
||||
}
|
||||
|
||||
$($impl_trait $Trait for Expr<$name> {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
type Output = Expr<$name>;
|
||||
fn $trait_fn(&self) -> Self::Output {
|
||||
*self
|
||||
}
|
||||
})?
|
||||
|
|
@ -171,13 +178,15 @@ reset_type!(
|
|||
);
|
||||
|
||||
impl ToSyncReset for bool {
|
||||
fn to_sync_reset(&self) -> Expr<SyncReset> {
|
||||
self.to_expr().to_sync_reset()
|
||||
type Output = SimValue<SyncReset>;
|
||||
fn to_sync_reset(&self) -> Self::Output {
|
||||
SimValue::from_value(SyncReset, *self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAsyncReset for bool {
|
||||
fn to_async_reset(&self) -> Expr<AsyncReset> {
|
||||
self.to_expr().to_async_reset()
|
||||
type Output = SimValue<AsyncReset>;
|
||||
fn to_async_reset(&self) -> Self::Output {
|
||||
SimValue::from_value(AsyncReset, *self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,14 +7,14 @@ use crate::{
|
|||
bundle::{BundleField, BundleType},
|
||||
enum_::{EnumType, EnumVariant},
|
||||
expr::{
|
||||
ExprEnum, Flow, ops,
|
||||
ExprEnum, Flow, ValueType, ops,
|
||||
target::{
|
||||
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField,
|
||||
TargetPathElement,
|
||||
},
|
||||
},
|
||||
int::BoolOrIntType,
|
||||
intern::{Intern, Interned, Memoize},
|
||||
intern::{Intern, InternSlice, Interned, Memoize},
|
||||
memory::PortKind,
|
||||
module::{
|
||||
AnnotatedModuleIO, Block, ExternModuleBody, Id, InstantiatedModule, ModuleBody, NameId,
|
||||
|
|
@ -28,12 +28,12 @@ use crate::{
|
|||
ExternModuleSimulation, SimTrace, SimTraceKind, SimTraces, TraceArray, TraceAsyncReset,
|
||||
TraceBool, TraceBundle, TraceClock, TraceDecl, TraceEnumDiscriminant, TraceEnumWithFields,
|
||||
TraceFieldlessEnum, TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId,
|
||||
TraceMemoryLocation, TraceModule, TraceModuleIO, TraceReg, TraceSInt, TraceScalarId,
|
||||
TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire,
|
||||
TraceMemoryLocation, TraceModule, TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt,
|
||||
TraceScalarId, TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire,
|
||||
interpreter::{
|
||||
Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding,
|
||||
InsnsBuildingDone, InsnsBuildingKind, Label, SmallUInt, StatePartArrayIndex,
|
||||
StatePartArrayIndexed,
|
||||
self, Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding,
|
||||
InsnsBuildingDone, InsnsBuildingKind, Label, PrefixLinesWrapper, SmallUInt,
|
||||
StatePartArrayIndex, StatePartArrayIndexed,
|
||||
parts::{
|
||||
MemoryData, SlotDebugData, StatePartIndex, StatePartIndexRange, StatePartKind,
|
||||
StatePartKindBigSlots, StatePartKindMemories, StatePartKindSimOnlySlots,
|
||||
|
|
@ -82,19 +82,73 @@ pub(crate) struct CompiledBundleField {
|
|||
pub(crate) ty: CompiledTypeLayout<CanonicalType>,
|
||||
}
|
||||
|
||||
impl CompiledBundleField {
|
||||
fn with_prefixed_debug_names(self, prefix: &str) -> Self {
|
||||
let Self { offset, ty } = self;
|
||||
Self {
|
||||
offset,
|
||||
ty: ty.with_prefixed_debug_names(prefix),
|
||||
}
|
||||
}
|
||||
fn with_anonymized_debug_info(self) -> Self {
|
||||
let Self { offset, ty } = self;
|
||||
Self {
|
||||
offset,
|
||||
ty: ty.with_anonymized_debug_info(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub(crate) enum CompiledTypeLayoutBody {
|
||||
Scalar,
|
||||
PhantomConst,
|
||||
Array {
|
||||
/// debug names are ignored, use parent's layout instead
|
||||
element: Interned<CompiledTypeLayout<CanonicalType>>,
|
||||
/// always has at least one element even for zero-sized arrays
|
||||
elements_non_empty: Interned<[CompiledTypeLayout<CanonicalType>]>,
|
||||
},
|
||||
Bundle {
|
||||
/// debug names are ignored, use parent's layout instead
|
||||
fields: Interned<[CompiledBundleField]>,
|
||||
},
|
||||
}
|
||||
|
||||
impl CompiledTypeLayoutBody {
|
||||
fn with_prefixed_debug_names(self, prefix: &str) -> Self {
|
||||
match self {
|
||||
CompiledTypeLayoutBody::Scalar | CompiledTypeLayoutBody::PhantomConst => self,
|
||||
CompiledTypeLayoutBody::Array { elements_non_empty } => CompiledTypeLayoutBody::Array {
|
||||
elements_non_empty: elements_non_empty
|
||||
.iter()
|
||||
.map(|element| element.with_prefixed_debug_names(prefix))
|
||||
.collect(),
|
||||
},
|
||||
CompiledTypeLayoutBody::Bundle { fields } => CompiledTypeLayoutBody::Bundle {
|
||||
fields: fields
|
||||
.iter()
|
||||
.map(|field| field.with_prefixed_debug_names(prefix))
|
||||
.collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
fn with_anonymized_debug_info(self) -> Self {
|
||||
match self {
|
||||
CompiledTypeLayoutBody::Scalar | CompiledTypeLayoutBody::PhantomConst => self,
|
||||
CompiledTypeLayoutBody::Array { elements_non_empty } => CompiledTypeLayoutBody::Array {
|
||||
elements_non_empty: elements_non_empty
|
||||
.iter()
|
||||
.map(|element| element.with_anonymized_debug_info())
|
||||
.collect(),
|
||||
},
|
||||
CompiledTypeLayoutBody::Bundle { fields } => CompiledTypeLayoutBody::Bundle {
|
||||
fields: fields
|
||||
.iter()
|
||||
.map(|field| field.with_anonymized_debug_info())
|
||||
.collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub(crate) struct CompiledTypeLayout<T: Type> {
|
||||
pub(crate) ty: T,
|
||||
|
|
@ -108,7 +162,7 @@ impl<T: Type> CompiledTypeLayout<T> {
|
|||
Self {
|
||||
ty,
|
||||
layout: layout.with_prefixed_debug_names(prefix),
|
||||
body,
|
||||
body: body.with_prefixed_debug_names(prefix),
|
||||
}
|
||||
}
|
||||
fn with_anonymized_debug_info(self) -> Self {
|
||||
|
|
@ -116,7 +170,7 @@ impl<T: Type> CompiledTypeLayout<T> {
|
|||
Self {
|
||||
ty,
|
||||
layout: layout.with_anonymized_debug_info(),
|
||||
body,
|
||||
body: body.with_anonymized_debug_info(),
|
||||
}
|
||||
}
|
||||
fn get(ty: T) -> Self {
|
||||
|
|
@ -151,28 +205,29 @@ impl<T: Type> CompiledTypeLayout<T> {
|
|||
}
|
||||
CanonicalType::Array(array) => {
|
||||
let mut layout = TypeLayout::empty();
|
||||
let element = CompiledTypeLayout::get(array.element()).intern_sized();
|
||||
let element = CompiledTypeLayout::get(array.element());
|
||||
let mut elements_non_empty = vec![];
|
||||
for index in 0..array.len() {
|
||||
layout.allocate(
|
||||
&element
|
||||
.layout
|
||||
.with_prefixed_debug_names(&format!("[{index}]")),
|
||||
);
|
||||
let element = element.with_prefixed_debug_names(&format!("[{index}]"));
|
||||
layout.allocate(&element.layout);
|
||||
elements_non_empty.push(element);
|
||||
}
|
||||
if array.is_empty() {
|
||||
elements_non_empty.push(element.with_prefixed_debug_names("[<none>]"));
|
||||
}
|
||||
CompiledTypeLayout {
|
||||
ty: *input,
|
||||
layout: layout.into(),
|
||||
body: CompiledTypeLayoutBody::Array { element },
|
||||
body: CompiledTypeLayoutBody::Array {
|
||||
elements_non_empty: elements_non_empty.intern_deref(),
|
||||
},
|
||||
}
|
||||
}
|
||||
CanonicalType::PhantomConst(_) => {
|
||||
let unit_layout = CompiledTypeLayout::get(());
|
||||
CompiledTypeLayout {
|
||||
CanonicalType::PhantomConst(_) => CompiledTypeLayout {
|
||||
ty: *input,
|
||||
layout: unit_layout.layout,
|
||||
body: unit_layout.body,
|
||||
}
|
||||
}
|
||||
layout: TypeLayout::empty(),
|
||||
body: CompiledTypeLayoutBody::PhantomConst,
|
||||
},
|
||||
CanonicalType::Bundle(bundle) => {
|
||||
let mut layout = TypeLayout::empty();
|
||||
let fields = bundle
|
||||
|
|
@ -184,13 +239,9 @@ impl<T: Type> CompiledTypeLayout<T> {
|
|||
flipped: _,
|
||||
ty,
|
||||
}| {
|
||||
let ty = CompiledTypeLayout::get(*ty);
|
||||
let offset = layout
|
||||
.allocate(
|
||||
&ty.layout
|
||||
.with_prefixed_debug_names(&format!(".{name}")),
|
||||
)
|
||||
.start();
|
||||
let ty = CompiledTypeLayout::get(*ty)
|
||||
.with_prefixed_debug_names(&format!(".{name}"));
|
||||
let offset = layout.allocate(&ty.layout).start();
|
||||
CompiledBundleField { offset, ty }
|
||||
},
|
||||
)
|
||||
|
|
@ -273,6 +324,39 @@ impl<T: Type> CompiledValue<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DebugCompiledValueStateAsMap<'a> {
|
||||
pub(crate) compiled_value: CompiledValue<CanonicalType>,
|
||||
pub(crate) state_layout: &'a interpreter::parts::StateLayout<InsnsBuildingDone>,
|
||||
pub(crate) state: &'a interpreter::State,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DebugCompiledValueStateAsMap<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use fmt::Write;
|
||||
if self.compiled_value.range.is_empty() {
|
||||
return f.write_str("{}");
|
||||
}
|
||||
writeln!(f, "{{")?;
|
||||
let mut f = PrefixLinesWrapper::new(f, true, " ");
|
||||
macro_rules! debug_fmt {
|
||||
(
|
||||
type_plural_fields = [$($type_plural_field:ident,)*];
|
||||
) => {
|
||||
$(for slot in self.compiled_value.range.$type_plural_field.iter() {
|
||||
slot.debug_fmt(&mut f, ":", " ", " ", "", Some(self.state_layout), Some(self.state))?;
|
||||
writeln!(f, ",")?;
|
||||
})*
|
||||
};
|
||||
}
|
||||
get_state_part_kinds! {
|
||||
debug_fmt! {
|
||||
type_plural_fields;
|
||||
}
|
||||
}
|
||||
write!(f.into_inner(), "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl CompiledValue<Bundle> {
|
||||
fn field_by_index(self, field_index: usize) -> CompiledValue<CanonicalType> {
|
||||
self.map(|layout, range| {
|
||||
|
|
@ -301,10 +385,13 @@ impl CompiledValue<Bundle> {
|
|||
impl CompiledValue<Array> {
|
||||
pub(crate) fn element(self, index: usize) -> CompiledValue<CanonicalType> {
|
||||
self.map(|layout, range| {
|
||||
let CompiledTypeLayoutBody::Array { element } = layout.body else {
|
||||
let CompiledTypeLayoutBody::Array { elements_non_empty } = layout.body else {
|
||||
unreachable!();
|
||||
};
|
||||
(*element, range.index_array(element.layout.len(), index))
|
||||
(
|
||||
elements_non_empty[index],
|
||||
range.index_array(elements_non_empty[index].layout.len(), index),
|
||||
)
|
||||
})
|
||||
}
|
||||
fn element_dyn(
|
||||
|
|
@ -557,10 +644,11 @@ impl CompiledExpr<Array> {
|
|||
self,
|
||||
index_slot: StatePartIndex<StatePartKindSmallSlots>,
|
||||
) -> CompiledExpr<CanonicalType> {
|
||||
let CompiledTypeLayoutBody::Array { element } = self.static_part.layout.body else {
|
||||
let CompiledTypeLayoutBody::Array { elements_non_empty } = self.static_part.layout.body
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
let stride = element.layout.len();
|
||||
let stride = elements_non_empty[0].layout.len();
|
||||
let indexes = self.indexes.join(TypeArrayIndex::from_parts(
|
||||
index_slot,
|
||||
self.static_part.layout.ty.len(),
|
||||
|
|
@ -568,10 +656,10 @@ impl CompiledExpr<Array> {
|
|||
));
|
||||
CompiledExpr {
|
||||
static_part: self.static_part.map(|layout, range| {
|
||||
let CompiledTypeLayoutBody::Array { element } = layout.body else {
|
||||
let CompiledTypeLayoutBody::Array { elements_non_empty } = layout.body else {
|
||||
unreachable!();
|
||||
};
|
||||
(*element, range.index_array(stride, 0))
|
||||
(elements_non_empty[0], range.index_array(stride, 0))
|
||||
}),
|
||||
indexes,
|
||||
}
|
||||
|
|
@ -1550,6 +1638,13 @@ struct ClockTrigger {
|
|||
source_location: SourceLocation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub(crate) struct ExternModuleClockForPast {
|
||||
pub(crate) clock_for_past: CompiledValue<Clock>,
|
||||
pub(crate) current_to_past_map:
|
||||
Interned<[(CompiledValue<CanonicalType>, CompiledValue<CanonicalType>)]>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Register {
|
||||
value: CompiledValue<CanonicalType>,
|
||||
|
|
@ -1619,7 +1714,7 @@ impl MakeTraceDeclTarget {
|
|||
}
|
||||
fn ty(self) -> CanonicalType {
|
||||
match self {
|
||||
MakeTraceDeclTarget::Expr(expr) => Expr::ty(expr),
|
||||
MakeTraceDeclTarget::Expr(expr) => expr.ty(),
|
||||
MakeTraceDeclTarget::Memory { ty, .. } => ty,
|
||||
}
|
||||
}
|
||||
|
|
@ -1637,7 +1732,9 @@ impl<T> fmt::Debug for DebugOpaque<T> {
|
|||
pub(crate) struct CompiledExternModule {
|
||||
pub(crate) module_io_targets: Interned<[Target]>,
|
||||
pub(crate) module_io: Interned<[CompiledValue<CanonicalType>]>,
|
||||
pub(crate) clocks_for_past: Interned<[ExternModuleClockForPast]>,
|
||||
pub(crate) simulation: ExternModuleSimulation,
|
||||
pub(crate) debug_name: Interned<str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -1681,12 +1778,16 @@ macro_rules! impl_compiler {
|
|||
instantiated_module: InstantiatedModule,
|
||||
target: MakeTraceDeclTarget,
|
||||
source_location: SourceLocation,
|
||||
empty_kind: impl FnOnce() -> SimTraceKind,
|
||||
$($type_singular_field: impl FnOnce(StatePartIndex<$type_kind>) -> SimTraceKind,)*
|
||||
) -> TraceLocation {
|
||||
match target {
|
||||
MakeTraceDeclTarget::Expr(target) => {
|
||||
let compiled_value = self.compile_expr(instantiated_module, target);
|
||||
let compiled_value = self.compiled_expr_to_value(compiled_value, source_location);
|
||||
if compiled_value.range.is_empty() {
|
||||
TraceLocation::Scalar(self.new_sim_trace(empty_kind()))
|
||||
} else {
|
||||
TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len().as_single() {
|
||||
$(Some(TypeLenSingle::$type_singular_variant) => {
|
||||
$type_singular_field(compiled_value.range.$type_plural_field.start)
|
||||
|
|
@ -1694,6 +1795,7 @@ macro_rules! impl_compiler {
|
|||
None => unreachable!(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
MakeTraceDeclTarget::Memory {
|
||||
id,
|
||||
depth,
|
||||
|
|
@ -1723,9 +1825,10 @@ macro_rules! impl_compiler {
|
|||
instantiated_module,
|
||||
target,
|
||||
source_location,
|
||||
|| unreachable!(),
|
||||
|index| SimTraceKind::SmallUInt { index, ty },
|
||||
|index| SimTraceKind::BigUInt { index, ty },
|
||||
|_| unreachable!(""),
|
||||
|_| unreachable!(),
|
||||
),
|
||||
name,
|
||||
ty,
|
||||
|
|
@ -1737,9 +1840,10 @@ macro_rules! impl_compiler {
|
|||
instantiated_module,
|
||||
target,
|
||||
source_location,
|
||||
|| unreachable!(),
|
||||
|index| SimTraceKind::SmallSInt { index, ty },
|
||||
|index| SimTraceKind::BigSInt { index, ty },
|
||||
|_| unreachable!(""),
|
||||
|_| unreachable!(),
|
||||
),
|
||||
name,
|
||||
ty,
|
||||
|
|
@ -1751,9 +1855,10 @@ macro_rules! impl_compiler {
|
|||
instantiated_module,
|
||||
target,
|
||||
source_location,
|
||||
|| unreachable!(),
|
||||
|index| SimTraceKind::SmallBool { index },
|
||||
|index| SimTraceKind::BigBool { index },
|
||||
|_| unreachable!(""),
|
||||
|_| unreachable!(),
|
||||
),
|
||||
name,
|
||||
flow,
|
||||
|
|
@ -1798,15 +1903,16 @@ macro_rules! impl_compiler {
|
|||
}
|
||||
.into()
|
||||
}
|
||||
CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
CanonicalType::Bundle(_) => unreachable!(),
|
||||
CanonicalType::AsyncReset(_) => TraceAsyncReset {
|
||||
location: self.make_trace_scalar_helper(
|
||||
instantiated_module,
|
||||
target,
|
||||
source_location,
|
||||
|| unreachable!(),
|
||||
|index| SimTraceKind::SmallAsyncReset { index },
|
||||
|index| SimTraceKind::BigAsyncReset { index },
|
||||
|_| unreachable!(""),
|
||||
|_| unreachable!(),
|
||||
),
|
||||
name,
|
||||
flow,
|
||||
|
|
@ -1817,9 +1923,10 @@ macro_rules! impl_compiler {
|
|||
instantiated_module,
|
||||
target,
|
||||
source_location,
|
||||
|| unreachable!(),
|
||||
|index| SimTraceKind::SmallSyncReset { index },
|
||||
|index| SimTraceKind::BigSyncReset { index },
|
||||
|_| unreachable!(""),
|
||||
|_| unreachable!(),
|
||||
),
|
||||
name,
|
||||
flow,
|
||||
|
|
@ -1831,21 +1938,38 @@ macro_rules! impl_compiler {
|
|||
instantiated_module,
|
||||
target,
|
||||
source_location,
|
||||
|| unreachable!(),
|
||||
|index| SimTraceKind::SmallClock { index },
|
||||
|index| SimTraceKind::BigClock { index },
|
||||
|_| unreachable!(""),
|
||||
|_| unreachable!(),
|
||||
),
|
||||
name,
|
||||
flow,
|
||||
}
|
||||
.into(),
|
||||
CanonicalType::PhantomConst(ty) => TracePhantomConst {
|
||||
location: self.make_trace_scalar_helper(
|
||||
instantiated_module,
|
||||
target,
|
||||
source_location,
|
||||
|| SimTraceKind::PhantomConst { ty },
|
||||
|_| unreachable!(),
|
||||
|_| unreachable!(),
|
||||
|_| unreachable!(),
|
||||
),
|
||||
name,
|
||||
ty,
|
||||
flow,
|
||||
}
|
||||
.into(),
|
||||
CanonicalType::DynSimOnly(ty) => TraceSimOnly {
|
||||
location: self.make_trace_scalar_helper(
|
||||
instantiated_module,
|
||||
target,
|
||||
source_location,
|
||||
|_| unreachable!(""),
|
||||
|_| unreachable!(""),
|
||||
|| unreachable!(),
|
||||
|_| unreachable!(),
|
||||
|_| unreachable!(),
|
||||
|index| SimTraceKind::SimOnly { index, ty },
|
||||
),
|
||||
name,
|
||||
|
|
@ -2295,16 +2419,10 @@ impl Compiler {
|
|||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::DynSimOnly(_) => {
|
||||
| CanonicalType::DynSimOnly(_)
|
||||
| CanonicalType::PhantomConst(_) => {
|
||||
self.make_trace_scalar(instantiated_module, target, name, source_location)
|
||||
}
|
||||
CanonicalType::PhantomConst(_) => TraceBundle {
|
||||
name,
|
||||
fields: Interned::default(),
|
||||
ty: Bundle::new(Interned::default()),
|
||||
flow: target.flow(),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
fn make_trace_decl(
|
||||
|
|
@ -2653,7 +2771,7 @@ impl Compiler {
|
|||
let retval = parts
|
||||
.into_iter()
|
||||
.map(|part| part.cast_to_bits())
|
||||
.reduce(|accumulator, part| accumulator | (part << Expr::ty(accumulator).width))
|
||||
.reduce(|accumulator, part| accumulator | (part << accumulator.ty().width))
|
||||
.unwrap_or_else(|| UInt[0].zero().to_expr());
|
||||
let retval = self.compile_expr(instantiated_module, Expr::canonical(retval));
|
||||
let retval = self
|
||||
|
|
@ -2665,7 +2783,7 @@ impl Compiler {
|
|||
instantiated_module: InstantiatedModule,
|
||||
expr: ops::CastToBits,
|
||||
) -> CompiledValue<UInt> {
|
||||
match Expr::ty(expr.arg()) {
|
||||
match expr.arg().ty() {
|
||||
CanonicalType::UInt(_) => {
|
||||
self.compile_cast_scalar_to_bits(instantiated_module, expr.arg(), |arg| arg)
|
||||
}
|
||||
|
|
@ -2831,7 +2949,7 @@ impl Compiler {
|
|||
return retval;
|
||||
}
|
||||
let mut cast_bit = |arg: Expr<CanonicalType>| {
|
||||
let src_signed = match Expr::ty(arg) {
|
||||
let src_signed = match arg.ty() {
|
||||
CanonicalType::UInt(_) => false,
|
||||
CanonicalType::SInt(_) => true,
|
||||
CanonicalType::Bool(_) => false,
|
||||
|
|
@ -2845,7 +2963,7 @@ impl Compiler {
|
|||
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
CanonicalType::DynSimOnly(_) => unreachable!(),
|
||||
};
|
||||
let dest_signed = match Expr::ty(expr) {
|
||||
let dest_signed = match expr.ty() {
|
||||
CanonicalType::UInt(_) => false,
|
||||
CanonicalType::SInt(_) => true,
|
||||
CanonicalType::Bool(_) => false,
|
||||
|
|
@ -2859,8 +2977,10 @@ impl Compiler {
|
|||
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
CanonicalType::DynSimOnly(_) => unreachable!(),
|
||||
};
|
||||
self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| {
|
||||
match (src_signed, dest_signed) {
|
||||
self.simple_nary_big_expr(instantiated_module, expr.ty(), [arg], |dest, [src]| match (
|
||||
src_signed,
|
||||
dest_signed,
|
||||
) {
|
||||
(false, false) | (true, true) => {
|
||||
vec![Insn::Copy { dest, src }]
|
||||
}
|
||||
|
|
@ -2874,7 +2994,6 @@ impl Compiler {
|
|||
src,
|
||||
dest_width: 1,
|
||||
}],
|
||||
}
|
||||
})
|
||||
.into()
|
||||
};
|
||||
|
|
@ -2914,21 +3033,13 @@ impl Compiler {
|
|||
})
|
||||
.into(),
|
||||
ExprEnum::PhantomConst(_) => self
|
||||
.compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default())
|
||||
.compile_aggregate_literal(instantiated_module, expr.ty(), Interned::default())
|
||||
.into(),
|
||||
ExprEnum::BundleLiteral(literal) => self
|
||||
.compile_aggregate_literal(
|
||||
instantiated_module,
|
||||
Expr::ty(expr),
|
||||
literal.field_values(),
|
||||
)
|
||||
.compile_aggregate_literal(instantiated_module, expr.ty(), literal.field_values())
|
||||
.into(),
|
||||
ExprEnum::ArrayLiteral(literal) => self
|
||||
.compile_aggregate_literal(
|
||||
instantiated_module,
|
||||
Expr::ty(expr),
|
||||
literal.element_values(),
|
||||
)
|
||||
.compile_aggregate_literal(instantiated_module, expr.ty(), literal.element_values())
|
||||
.into(),
|
||||
ExprEnum::EnumLiteral(expr) => {
|
||||
let enum_bits_ty = UInt[expr.ty().type_properties().bit_width];
|
||||
|
|
@ -2956,13 +3067,13 @@ impl Compiler {
|
|||
ExprEnum::NotU(expr) => self
|
||||
.simple_nary_big_expr(
|
||||
instantiated_module,
|
||||
Expr::ty(expr.arg()).canonical(),
|
||||
expr.arg().ty().canonical(),
|
||||
[Expr::canonical(expr.arg())],
|
||||
|dest, [src]| {
|
||||
vec![Insn::NotU {
|
||||
dest,
|
||||
src,
|
||||
width: Expr::ty(expr.arg()).width(),
|
||||
width: expr.arg().ty().width(),
|
||||
}]
|
||||
},
|
||||
)
|
||||
|
|
@ -2970,7 +3081,7 @@ impl Compiler {
|
|||
ExprEnum::NotS(expr) => self
|
||||
.simple_nary_big_expr(
|
||||
instantiated_module,
|
||||
Expr::ty(expr.arg()).canonical(),
|
||||
expr.arg().ty().canonical(),
|
||||
[Expr::canonical(expr.arg())],
|
||||
|dest, [src]| vec![Insn::NotS { dest, src }],
|
||||
)
|
||||
|
|
@ -2978,7 +3089,7 @@ impl Compiler {
|
|||
ExprEnum::NotB(expr) => self
|
||||
.simple_nary_big_expr(
|
||||
instantiated_module,
|
||||
Expr::ty(expr.arg()).canonical(),
|
||||
expr.arg().ty().canonical(),
|
||||
[Expr::canonical(expr.arg())],
|
||||
|dest, [src]| {
|
||||
vec![Insn::NotU {
|
||||
|
|
@ -3482,12 +3593,12 @@ impl Compiler {
|
|||
.map_ty(Bundle::from_canonical)
|
||||
.field_by_index(expr.field_index()),
|
||||
ExprEnum::VariantAccess(variant_access) => {
|
||||
let start = Expr::ty(variant_access.base()).discriminant_bit_width();
|
||||
let len = Expr::ty(expr).bit_width();
|
||||
let start = variant_access.base().ty().discriminant_bit_width();
|
||||
let len = expr.ty().bit_width();
|
||||
self.compile_expr(
|
||||
instantiated_module,
|
||||
variant_access.base().cast_to_bits()[start..start + len]
|
||||
.cast_bits_to(Expr::ty(expr)),
|
||||
.cast_bits_to(expr.ty()),
|
||||
)
|
||||
}
|
||||
ExprEnum::ArrayIndex(expr) => self
|
||||
|
|
@ -3509,45 +3620,39 @@ impl Compiler {
|
|||
.map_ty(Array::from_canonical)
|
||||
.element_dyn(index_slot)
|
||||
}
|
||||
ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 {
|
||||
ExprEnum::ReduceBitAndU(expr) => if expr.arg().ty().width() == 0 {
|
||||
self.compile_expr(instantiated_module, Expr::canonical(true.to_expr()))
|
||||
} else {
|
||||
self.compile_expr(
|
||||
instantiated_module,
|
||||
Expr::canonical(
|
||||
expr.arg()
|
||||
.cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)),
|
||||
),
|
||||
Expr::canonical(expr.arg().cmp_eq(expr.arg().ty().from_int_wrapping(-1))),
|
||||
)
|
||||
}
|
||||
.into(),
|
||||
ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 {
|
||||
ExprEnum::ReduceBitAndS(expr) => if expr.arg().ty().width() == 0 {
|
||||
self.compile_expr(instantiated_module, Expr::canonical(true.to_expr()))
|
||||
} else {
|
||||
self.compile_expr(
|
||||
instantiated_module,
|
||||
Expr::canonical(
|
||||
expr.arg()
|
||||
.cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)),
|
||||
),
|
||||
Expr::canonical(expr.arg().cmp_eq(expr.arg().ty().from_int_wrapping(-1))),
|
||||
)
|
||||
}
|
||||
.into(),
|
||||
ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 {
|
||||
ExprEnum::ReduceBitOrU(expr) => if expr.arg().ty().width() == 0 {
|
||||
self.compile_expr(instantiated_module, Expr::canonical(false.to_expr()))
|
||||
} else {
|
||||
self.compile_expr(
|
||||
instantiated_module,
|
||||
Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))),
|
||||
Expr::canonical(expr.arg().cmp_ne(expr.arg().ty().from_int_wrapping(0))),
|
||||
)
|
||||
}
|
||||
.into(),
|
||||
ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 {
|
||||
ExprEnum::ReduceBitOrS(expr) => if expr.arg().ty().width() == 0 {
|
||||
self.compile_expr(instantiated_module, Expr::canonical(false.to_expr()))
|
||||
} else {
|
||||
self.compile_expr(
|
||||
instantiated_module,
|
||||
Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))),
|
||||
Expr::canonical(expr.arg().cmp_ne(expr.arg().ty().from_int_wrapping(0))),
|
||||
)
|
||||
}
|
||||
.into(),
|
||||
|
|
@ -3560,7 +3665,7 @@ impl Compiler {
|
|||
vec![Insn::ReduceBitXor {
|
||||
dest,
|
||||
src,
|
||||
input_width: Expr::ty(expr.arg()).width(),
|
||||
input_width: expr.arg().ty().width(),
|
||||
}]
|
||||
},
|
||||
)
|
||||
|
|
@ -3574,7 +3679,7 @@ impl Compiler {
|
|||
vec![Insn::ReduceBitXor {
|
||||
dest,
|
||||
src,
|
||||
input_width: Expr::ty(expr.arg()).width(),
|
||||
input_width: expr.arg().ty().width(),
|
||||
}]
|
||||
},
|
||||
)
|
||||
|
|
@ -3672,8 +3777,8 @@ impl Compiler {
|
|||
mut rhs: Expr<CanonicalType>,
|
||||
source_location: SourceLocation,
|
||||
) {
|
||||
if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() {
|
||||
match Expr::ty(lhs) {
|
||||
if lhs.ty() != rhs.ty() || !lhs.ty().is_passive() {
|
||||
match lhs.ty() {
|
||||
CanonicalType::UInt(lhs_ty) => {
|
||||
rhs = Expr::canonical(Expr::<UInt>::from_canonical(rhs).cast_to(lhs_ty));
|
||||
}
|
||||
|
|
@ -3682,7 +3787,7 @@ impl Compiler {
|
|||
}
|
||||
CanonicalType::Bool(_) => unreachable!(),
|
||||
CanonicalType::Array(lhs_ty) => {
|
||||
let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else {
|
||||
let CanonicalType::Array(rhs_ty) = rhs.ty() else {
|
||||
unreachable!();
|
||||
};
|
||||
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||
|
|
@ -3702,13 +3807,13 @@ impl Compiler {
|
|||
return;
|
||||
}
|
||||
CanonicalType::Enum(lhs_ty) => {
|
||||
let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else {
|
||||
let CanonicalType::Enum(rhs_ty) = rhs.ty() else {
|
||||
unreachable!();
|
||||
};
|
||||
todo!("handle connect with different enum types");
|
||||
}
|
||||
CanonicalType::Bundle(lhs_ty) => {
|
||||
let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else {
|
||||
let CanonicalType::Bundle(rhs_ty) = rhs.ty() else {
|
||||
unreachable!();
|
||||
};
|
||||
assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len());
|
||||
|
|
@ -3896,18 +4001,15 @@ impl Compiler {
|
|||
self.enum_discriminants.insert(enum_value, retval);
|
||||
retval
|
||||
}
|
||||
fn compile_stmt_reg<R: ResetType>(
|
||||
fn compile_reg<R: ResetType>(
|
||||
&mut self,
|
||||
stmt_reg: StmtReg<R>,
|
||||
clk: CompiledValue<Clock>,
|
||||
reset_and_init: Option<(Expr<R>, CompiledValue<CanonicalType>)>,
|
||||
source_location: SourceLocation,
|
||||
instantiated_module: InstantiatedModule,
|
||||
value: CompiledValue<CanonicalType>,
|
||||
) {
|
||||
let StmtReg { annotations, reg } = stmt_reg;
|
||||
let clk = self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().clk));
|
||||
let clk = self
|
||||
.compiled_expr_to_value(clk, reg.source_location())
|
||||
.map_ty(Clock::from_canonical);
|
||||
let clk = self.compile_clock(clk, reg.source_location());
|
||||
let clk = self.compile_clock(clk, source_location);
|
||||
struct Dispatch;
|
||||
impl ResetTypeDispatch for Dispatch {
|
||||
type Input<T: ResetType> = ();
|
||||
|
|
@ -3926,18 +4028,15 @@ impl Compiler {
|
|||
true
|
||||
}
|
||||
}
|
||||
let reset = if let Some(init) = reg.init() {
|
||||
let init = self.compile_expr(instantiated_module, init);
|
||||
let init = self.compiled_expr_to_value(init, reg.source_location());
|
||||
let rst =
|
||||
self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().rst));
|
||||
let rst = self.compiled_expr_to_value(rst, reg.source_location());
|
||||
let rst = self.compiled_value_bool_dest_is_small(rst, reg.source_location());
|
||||
let reset = if let Some((rst_expr, init)) = reset_and_init {
|
||||
let rst = self.compile_expr(instantiated_module, Expr::canonical(rst_expr));
|
||||
let rst = self.compiled_expr_to_value(rst, source_location);
|
||||
let rst = self.compiled_value_bool_dest_is_small(rst, source_location);
|
||||
let is_async = R::dispatch((), Dispatch);
|
||||
if is_async {
|
||||
let cond = Expr::canonical(reg.clock_domain().rst.cast_to(Bool));
|
||||
let cond = Expr::canonical(rst_expr.cast_to(Bool));
|
||||
let cond = self.compile_expr(instantiated_module, cond);
|
||||
let cond = self.compiled_expr_to_value(cond, reg.source_location());
|
||||
let cond = self.compiled_expr_to_value(cond, source_location);
|
||||
let cond = cond.map_ty(Bool::from_canonical);
|
||||
// write to the register's current value since asynchronous reset is combinational
|
||||
let lhs = CompiledValue {
|
||||
|
|
@ -3949,12 +4048,12 @@ impl Compiler {
|
|||
self.compile_simple_connect(
|
||||
[Cond {
|
||||
body: CondBody::IfTrue { cond },
|
||||
source_location: reg.source_location(),
|
||||
}][..]
|
||||
.intern(),
|
||||
source_location: source_location,
|
||||
}]
|
||||
.intern_slice(),
|
||||
lhs,
|
||||
init,
|
||||
reg.source_location(),
|
||||
source_location,
|
||||
);
|
||||
}
|
||||
Some(RegisterReset {
|
||||
|
|
@ -3969,9 +4068,33 @@ impl Compiler {
|
|||
value,
|
||||
clk_triggered: clk.clk_triggered,
|
||||
reset,
|
||||
source_location: reg.source_location(),
|
||||
source_location,
|
||||
});
|
||||
}
|
||||
fn compile_stmt_reg<R: ResetType>(
|
||||
&mut self,
|
||||
stmt_reg: StmtReg<R>,
|
||||
instantiated_module: InstantiatedModule,
|
||||
value: CompiledValue<CanonicalType>,
|
||||
) {
|
||||
let StmtReg { annotations, reg } = stmt_reg;
|
||||
let clk = self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().clk));
|
||||
let clk = self
|
||||
.compiled_expr_to_value(clk, reg.source_location())
|
||||
.map_ty(Clock::from_canonical);
|
||||
let reset_and_init = reg.init().map(|init| {
|
||||
let init = self.compile_expr(instantiated_module, init);
|
||||
let init = self.compiled_expr_to_value(init, reg.source_location());
|
||||
(reg.clock_domain().rst, init)
|
||||
});
|
||||
self.compile_reg(
|
||||
clk,
|
||||
reset_and_init,
|
||||
reg.source_location(),
|
||||
instantiated_module,
|
||||
value,
|
||||
);
|
||||
}
|
||||
fn compile_declaration(
|
||||
&mut self,
|
||||
declaration: StmtDeclaration,
|
||||
|
|
@ -4237,24 +4360,24 @@ impl Compiler {
|
|||
insns.push(end_label.into());
|
||||
}
|
||||
}
|
||||
CompiledTypeLayoutBody::Array { element } => {
|
||||
CompiledTypeLayoutBody::Array { elements_non_empty } => {
|
||||
let CompiledTypeLayoutBody::Array {
|
||||
element: mask_element,
|
||||
elements_non_empty: mask_elements_non_empty,
|
||||
} = mask_layout.body
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
let ty = <Array>::from_canonical(data_layout.ty);
|
||||
let element_bit_width = ty.element().bit_width();
|
||||
let element_size = element.layout.len();
|
||||
let mask_element_size = mask_element.layout.len();
|
||||
let element_size = elements_non_empty[0].layout.len();
|
||||
let mask_element_size = mask_elements_non_empty[0].layout.len();
|
||||
for element_index in 0..ty.len() {
|
||||
self.compile_memory_port_rw_helper(
|
||||
memory,
|
||||
stride,
|
||||
start,
|
||||
*element,
|
||||
*mask_element,
|
||||
elements_non_empty[element_index],
|
||||
mask_elements_non_empty[element_index],
|
||||
read.as_mut().map(
|
||||
|MemoryPortReadInsns {
|
||||
addr,
|
||||
|
|
@ -4293,6 +4416,7 @@ impl Compiler {
|
|||
start += element_bit_width;
|
||||
}
|
||||
}
|
||||
CompiledTypeLayoutBody::PhantomConst => {}
|
||||
CompiledTypeLayoutBody::Bundle { fields } => {
|
||||
let CompiledTypeLayoutBody::Bundle {
|
||||
fields: mask_fields,
|
||||
|
|
@ -4860,6 +4984,88 @@ impl Compiler {
|
|||
}
|
||||
}
|
||||
}
|
||||
fn compile_extern_module_clock_for_past(
|
||||
&mut self,
|
||||
instantiated_module: InstantiatedModule,
|
||||
clock_for_past: Target,
|
||||
) -> ExternModuleClockForPast {
|
||||
let clock_for_past = TargetInInstantiatedModule {
|
||||
instantiated_module,
|
||||
target: clock_for_past,
|
||||
};
|
||||
let clock_for_past = self
|
||||
.compile_value(clock_for_past)
|
||||
.map_ty(Clock::from_canonical);
|
||||
let clock_for_past_debug_name = match clock_for_past
|
||||
.range
|
||||
.len()
|
||||
.as_single()
|
||||
.expect("Clock is a single slot")
|
||||
{
|
||||
TypeLenSingle::BigSlot => {
|
||||
self.insns
|
||||
.state_layout
|
||||
.ty
|
||||
.big_slots
|
||||
.debug_data(clock_for_past.range.start().big_slots)
|
||||
.name
|
||||
}
|
||||
TypeLenSingle::SmallSlot => {
|
||||
self.insns
|
||||
.state_layout
|
||||
.ty
|
||||
.small_slots
|
||||
.debug_data(clock_for_past.range.start().small_slots)
|
||||
.name
|
||||
}
|
||||
TypeLenSingle::SimOnlySlot => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
let module_prefix = format!("{instantiated_module:?}.");
|
||||
let trimmed_clock_for_past_debug_name = clock_for_past_debug_name
|
||||
.strip_prefix(&module_prefix)
|
||||
.unwrap_or(&clock_for_past_debug_name);
|
||||
let current_to_past_map = instantiated_module
|
||||
.leaf_module()
|
||||
.module_io()
|
||||
.iter()
|
||||
.map(
|
||||
|&AnnotatedModuleIO {
|
||||
annotations: _,
|
||||
module_io,
|
||||
}| {
|
||||
let target_base = TargetBase::from(module_io);
|
||||
let current = self.compile_value(TargetInInstantiatedModule {
|
||||
instantiated_module,
|
||||
target: target_base.into(),
|
||||
});
|
||||
let unprefixed_layout = CompiledTypeLayout::get(module_io.ty());
|
||||
let past_layout = unprefixed_layout.with_prefixed_debug_names(&format!(
|
||||
"{module_prefix}{:?}$past({trimmed_clock_for_past_debug_name})",
|
||||
target_base.target_name(),
|
||||
));
|
||||
let past = CompiledValue {
|
||||
range: self.insns.allocate_variable(&past_layout.layout),
|
||||
layout: past_layout,
|
||||
write: Some((current.layout, current.range)),
|
||||
};
|
||||
self.compile_reg::<SyncReset>(
|
||||
clock_for_past,
|
||||
None,
|
||||
module_io.source_location(),
|
||||
instantiated_module,
|
||||
past,
|
||||
);
|
||||
(current, past)
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
ExternModuleClockForPast {
|
||||
clock_for_past,
|
||||
current_to_past_map,
|
||||
}
|
||||
}
|
||||
fn compile_module(&mut self, module: Interned<InstantiatedModule>) -> &CompiledModule {
|
||||
let mut trace_decls = Vec::new();
|
||||
let module_io = module
|
||||
|
|
@ -4888,6 +5094,7 @@ impl Compiler {
|
|||
ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name: _,
|
||||
parameters: _,
|
||||
clocks_for_past,
|
||||
simulation,
|
||||
}) => {
|
||||
let Some(simulation) = simulation else {
|
||||
|
|
@ -4904,10 +5111,18 @@ impl Compiler {
|
|||
Target::from(*simulation.sim_io_to_generator_map[&v.module_io.intern()])
|
||||
})
|
||||
.collect();
|
||||
let clocks_for_past = clocks_for_past
|
||||
.iter()
|
||||
.map(|clock_for_past| {
|
||||
self.compile_extern_module_clock_for_past(*module, *clock_for_past)
|
||||
})
|
||||
.collect();
|
||||
self.extern_modules.push(CompiledExternModule {
|
||||
module_io_targets,
|
||||
module_io,
|
||||
clocks_for_past,
|
||||
simulation,
|
||||
debug_name: format!("{module:?}").intern_deref(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::ValueType,
|
||||
int::{BoolOrIntType, SInt, UInt},
|
||||
intern::{Intern, Interned, Memoize},
|
||||
sim::interpreter::parts::{
|
||||
|
|
@ -196,13 +197,27 @@ impl fmt::Debug for Insn {
|
|||
}
|
||||
}
|
||||
|
||||
struct PrefixLinesWrapper<'a, W> {
|
||||
pub(crate) struct PrefixLinesWrapper<'a, W> {
|
||||
writer: W,
|
||||
at_beginning_of_line: bool,
|
||||
blank_line_prefix: &'a str,
|
||||
line_prefix: &'a str,
|
||||
}
|
||||
|
||||
impl<'a, W> PrefixLinesWrapper<'a, W> {
|
||||
pub(crate) fn new(writer: W, at_beginning_of_line: bool, line_prefix: &'a str) -> Self {
|
||||
Self {
|
||||
writer,
|
||||
at_beginning_of_line,
|
||||
blank_line_prefix: line_prefix.trim_end(),
|
||||
line_prefix,
|
||||
}
|
||||
}
|
||||
pub(crate) fn into_inner(self) -> W {
|
||||
self.writer
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Write> fmt::Write for PrefixLinesWrapper<'_, T> {
|
||||
fn write_str(&mut self, input: &str) -> fmt::Result {
|
||||
for part in input.split_inclusive('\n') {
|
||||
|
|
@ -239,12 +254,7 @@ impl Insn {
|
|||
if fields.len() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let mut f = PrefixLinesWrapper {
|
||||
writer: f,
|
||||
at_beginning_of_line: false,
|
||||
blank_line_prefix: "",
|
||||
line_prefix: " ",
|
||||
};
|
||||
let mut f = PrefixLinesWrapper::new(f, false, " ");
|
||||
writeln!(f, " {{")?;
|
||||
for (field_name, field) in fields {
|
||||
write!(f, "{field_name}: ")?;
|
||||
|
|
@ -320,7 +330,7 @@ impl Insn {
|
|||
}
|
||||
writeln!(f, ",")?;
|
||||
}
|
||||
write!(f.writer, "}}")
|
||||
write!(f.into_inner(), "}}")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
Insn, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, PrefixLinesWrapper,
|
||||
SmallSInt, SmallUInt, State,
|
||||
},
|
||||
value::{DynSimOnlyValue, DynSimOnly},
|
||||
value::{DynSimOnly, DynSimOnlyValue},
|
||||
},
|
||||
ty::CanonicalType,
|
||||
util::{chain, const_str_cmp},
|
||||
|
|
@ -435,12 +435,7 @@ impl<K: StatePartKind> StatePartIndex<K> {
|
|||
if state.is_some() || debug_data.is_some() {
|
||||
f.write_str(comment_start)?;
|
||||
}
|
||||
let mut f = PrefixLinesWrapper {
|
||||
writer: f,
|
||||
at_beginning_of_line: false,
|
||||
blank_line_prefix: comment_line_start.trim_end(),
|
||||
line_prefix: comment_line_start,
|
||||
};
|
||||
let mut f = PrefixLinesWrapper::new(f, false, comment_line_start);
|
||||
if let Some(state) = state {
|
||||
f.write_str("(")?;
|
||||
K::debug_fmt_state_value(state, *self, &mut f)?;
|
||||
|
|
@ -453,7 +448,7 @@ impl<K: StatePartKind> StatePartIndex<K> {
|
|||
write!(f, "{debug_data:?}")?;
|
||||
}
|
||||
if state.is_some() || debug_data.is_some() {
|
||||
f.writer.write_str(comment_end)?;
|
||||
f.into_inner().write_str(comment_end)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@ use crate::{
|
|||
bundle::{Bundle, BundleType},
|
||||
clock::Clock,
|
||||
enum_::{Enum, EnumType},
|
||||
expr::{CastBitsTo, Expr, ToExpr},
|
||||
expr::{
|
||||
CastBitsTo, Expr, HdlPartialEq, HdlPartialEqImpl, HdlPartialOrd, ToExpr, ValueType,
|
||||
value_category::{ValueCategorySimValue, ValueCategoryValue},
|
||||
},
|
||||
int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue},
|
||||
reset::{AsyncReset, Reset, SyncReset},
|
||||
source_location::SourceLocation,
|
||||
|
|
@ -23,10 +26,11 @@ use bitvec::{slice::BitSlice, vec::BitVec};
|
|||
use hashbrown::hash_map::Entry;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
borrow::{Borrow, BorrowMut, Cow},
|
||||
fmt::{self, Write},
|
||||
hash::{BuildHasher, Hash, Hasher, RandomState},
|
||||
ops::{Deref, DerefMut},
|
||||
num::NonZero,
|
||||
ops::{Deref, DerefMut, Index, IndexMut},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
|
|
@ -136,7 +140,7 @@ impl<T: Type<SimValue: Serialize> + Serialize> Serialize for SimValue<T> {
|
|||
S: Serializer,
|
||||
{
|
||||
SerdeSimValue {
|
||||
ty: SimValue::ty(self),
|
||||
ty: self.ty(),
|
||||
value: std::borrow::Cow::Borrowed(&*self),
|
||||
}
|
||||
.serialize(serializer)
|
||||
|
|
@ -157,7 +161,22 @@ pub struct SimValue<T: Type> {
|
|||
inner: AlternatingCell<SimValueInner<T>>,
|
||||
}
|
||||
|
||||
impl<T: Type + Clone> Clone for SimValue<T> {
|
||||
impl<T: Type> ValueType 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 {
|
||||
Self {
|
||||
inner: AlternatingCell::new_unique(self.inner.share().clone()),
|
||||
|
|
@ -212,9 +231,6 @@ impl<T: Type> SimValue<T> {
|
|||
inner: AlternatingCell::new_unique(inner),
|
||||
}
|
||||
}
|
||||
pub fn ty(this: &Self) -> T {
|
||||
this.inner.share().ty
|
||||
}
|
||||
pub fn into_opaque(this: Self) -> OpaqueSimValue {
|
||||
this.inner.into_inner().into_opaque()
|
||||
}
|
||||
|
|
@ -253,7 +269,7 @@ impl<T: Type> SimValue<T> {
|
|||
SimValue::from_opaque(ty.canonical(), opaque)
|
||||
}
|
||||
pub fn canonical(this: &Self) -> SimValue<CanonicalType> {
|
||||
SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone())
|
||||
SimValue::from_opaque(this.ty().canonical(), Self::opaque(this).clone())
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self
|
||||
|
|
@ -274,7 +290,7 @@ impl<T: Type> SimValue<T> {
|
|||
where
|
||||
T: IntType,
|
||||
{
|
||||
SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone())
|
||||
SimValue::from_opaque(this.ty().as_dyn_int(), Self::opaque(&this).clone())
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn from_bundle(v: SimValue<Bundle>) -> Self
|
||||
|
|
@ -296,7 +312,7 @@ impl<T: Type> SimValue<T> {
|
|||
T: BundleType,
|
||||
{
|
||||
SimValue::from_opaque(
|
||||
Bundle::from_canonical(Self::ty(this).canonical()),
|
||||
Bundle::from_canonical(this.ty().canonical()),
|
||||
Self::opaque(&this).clone(),
|
||||
)
|
||||
}
|
||||
|
|
@ -320,7 +336,7 @@ impl<T: Type> SimValue<T> {
|
|||
T: EnumType,
|
||||
{
|
||||
SimValue::from_opaque(
|
||||
Enum::from_canonical(Self::ty(this).canonical()),
|
||||
Enum::from_canonical(this.ty().canonical()),
|
||||
Self::opaque(&this).clone(),
|
||||
)
|
||||
}
|
||||
|
|
@ -351,8 +367,6 @@ impl<T: Type> fmt::Debug for SimValue<T> {
|
|||
}
|
||||
|
||||
impl<T: Type> ToExpr for SimValue<T> {
|
||||
type Type = T;
|
||||
|
||||
#[track_caller]
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
let inner = self.inner.share();
|
||||
|
|
@ -360,43 +374,297 @@ impl<T: Type> ToExpr for SimValue<T> {
|
|||
inner.sim_only_values_len, 0,
|
||||
"can't convert sim-only values to Expr"
|
||||
);
|
||||
inner.opaque.bits().cast_bits_to(inner.ty)
|
||||
inner.opaque.bits().to_expr().cast_bits_to(inner.ty)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SimValuePartialEq<T: Type = Self>: Type {
|
||||
#[track_caller]
|
||||
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<T>) -> bool;
|
||||
impl<T: Type, Len: Size, I> Index<I> for SimValue<ArrayType<T, Len>>
|
||||
where
|
||||
[SimValue<T>]: Index<I>,
|
||||
{
|
||||
type Output = <[SimValue<T>] as Index<I>>::Output;
|
||||
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
(**self).borrow().index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SimValuePartialEq<U>, U: Type> PartialEq<SimValue<U>> for SimValue<T> {
|
||||
impl<T: Type, Len: Size, I> IndexMut<I> for SimValue<ArrayType<T, Len>>
|
||||
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]
|
||||
fn eq(&self, other: &SimValue<U>) -> bool {
|
||||
T::sim_value_eq(self, other)
|
||||
*self.cmp_eq(other)
|
||||
}
|
||||
#[track_caller]
|
||||
fn ne(&self, other: &SimValue<U>) -> bool {
|
||||
*self.cmp_ne(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> SimValuePartialEq<Self> for UIntType<Width> {
|
||||
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
|
||||
**this == **other
|
||||
pub trait SimValueEq: Type
|
||||
where
|
||||
for<'a> SimValue<Self>: HdlPartialEq<&'a SimValue<Self>, Output = SimValue<Bool>>,
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> SimValuePartialEq<Self> for SIntType<Width> {
|
||||
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
|
||||
**this == **other
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
impl SimValuePartialEq<Bool> for Bool {
|
||||
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Bool>) -> bool {
|
||||
**this == **other
|
||||
pub trait SimValueOrd: SimValueEq
|
||||
where
|
||||
for<'a> SimValue<Self>: HdlPartialOrd<&'a SimValue<Self>, Output = SimValue<Bool>>,
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> {
|
||||
type Type: Type;
|
||||
impl<Width: Size> SimValueEq for UIntType<Width> {}
|
||||
|
||||
impl<Width: Size> SimValueOrd for UIntType<Width> {}
|
||||
|
||||
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);
|
||||
impl_sim_value_cmp_as_bool!(Clock);
|
||||
impl_sim_value_cmp_as_bool!(Reset);
|
||||
impl_sim_value_cmp_as_bool!(SyncReset);
|
||||
impl_sim_value_cmp_as_bool!(AsyncReset);
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod match_sim_value {
|
||||
use crate::{
|
||||
sim::value::{SimValue, ToSimValue},
|
||||
ty::Type,
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct MatchSimValueHelper<T>(Option<T>);
|
||||
|
||||
impl<T> MatchSimValueHelper<T> {
|
||||
pub fn new(v: T) -> Self {
|
||||
Self(Some(v))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait MatchSimValue {
|
||||
type MatchValue;
|
||||
|
||||
/// use `self` so it comes first in the method resolution order
|
||||
fn __fayalite_match_sim_value(self) -> Self::MatchValue
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl<T: Type> MatchSimValue for MatchSimValueHelper<SimValue<T>> {
|
||||
type MatchValue = T::SimValue;
|
||||
|
||||
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
|
||||
SimValue::into_value(self.0.expect("should be Some"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a SimValue<T>> {
|
||||
type MatchValue = &'a T::SimValue;
|
||||
|
||||
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
|
||||
SimValue::value(self.0.expect("should be Some"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a mut SimValue<T>> {
|
||||
type MatchValue = &'a mut T::SimValue;
|
||||
|
||||
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
|
||||
SimValue::value_mut(self.0.expect("should be Some"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ &'a T>
|
||||
where
|
||||
MatchSimValueHelper<&'a T>: MatchSimValue,
|
||||
{
|
||||
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
|
||||
|
||||
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
|
||||
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ mut &'a T>
|
||||
where
|
||||
MatchSimValueHelper<&'a T>: MatchSimValue,
|
||||
{
|
||||
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
|
||||
|
||||
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
|
||||
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a &'_ mut T>
|
||||
where
|
||||
MatchSimValueHelper<&'a T>: MatchSimValue,
|
||||
{
|
||||
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
|
||||
|
||||
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
|
||||
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &**v)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a mut &'_ mut T>
|
||||
where
|
||||
MatchSimValueHelper<&'a mut T>: MatchSimValue,
|
||||
{
|
||||
type MatchValue = <MatchSimValueHelper<&'a mut T> as MatchSimValue>::MatchValue;
|
||||
|
||||
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
|
||||
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &mut **v)))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait MatchSimValueFallback {
|
||||
type MatchValue;
|
||||
|
||||
/// use `&mut self` so it comes later in the method resolution order than MatchSimValue
|
||||
fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue;
|
||||
}
|
||||
|
||||
impl<T: ToSimValue> MatchSimValueFallback for MatchSimValueHelper<T> {
|
||||
type MatchValue = <T::Type as Type>::SimValue;
|
||||
|
||||
fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue {
|
||||
SimValue::into_value(self.0.take().expect("should be Some").into_sim_value())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToSimValue: ToSimValueWithType<<Self as ValueType>::Type> + ValueType {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type>;
|
||||
#[track_caller]
|
||||
|
|
@ -438,31 +706,31 @@ pub trait ToSimValueWithType<T: Type> {
|
|||
|
||||
macro_rules! forward_to_sim_value_with_type {
|
||||
([$($generics:tt)*] $ty:ty) => {
|
||||
impl<$($generics)*> ToSimValueWithType<<Self as ToSimValue>::Type> for $ty {
|
||||
fn to_sim_value_with_type(&self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
|
||||
impl<$($generics)*> ToSimValueWithType<<Self as ValueType>::Type> for $ty {
|
||||
fn to_sim_value_with_type(&self, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
|
||||
let retval = Self::to_sim_value(self);
|
||||
assert_eq!(SimValue::ty(&retval), ty);
|
||||
assert_eq!(retval.ty(), ty);
|
||||
retval
|
||||
}
|
||||
#[track_caller]
|
||||
fn into_sim_value_with_type(self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type>
|
||||
fn into_sim_value_with_type(self, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let retval = Self::into_sim_value(self);
|
||||
assert_eq!(SimValue::ty(&retval), ty);
|
||||
assert_eq!(retval.ty(), ty);
|
||||
retval
|
||||
}
|
||||
#[track_caller]
|
||||
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
|
||||
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
|
||||
let retval = Self::arc_into_sim_value(self);
|
||||
assert_eq!(SimValue::ty(&retval), ty);
|
||||
assert_eq!(retval.ty(), ty);
|
||||
retval
|
||||
}
|
||||
#[track_caller]
|
||||
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
|
||||
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
|
||||
let retval = Self::arc_to_sim_value(self);
|
||||
assert_eq!(SimValue::ty(&retval), ty);
|
||||
assert_eq!(retval.ty(), ty);
|
||||
retval
|
||||
}
|
||||
}
|
||||
|
|
@ -470,7 +738,6 @@ macro_rules! forward_to_sim_value_with_type {
|
|||
}
|
||||
|
||||
impl<T: Type> ToSimValue for SimValue<T> {
|
||||
type Type = T;
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
self.clone()
|
||||
}
|
||||
|
|
@ -526,9 +793,7 @@ impl<T: Type> ToSimValueWithType<T> for BitSlice {
|
|||
}
|
||||
}
|
||||
|
||||
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ This {
|
||||
type Type = This::Type;
|
||||
|
||||
impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This {
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
This::to_sim_value(self)
|
||||
}
|
||||
|
|
@ -541,8 +806,6 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'
|
|||
}
|
||||
|
||||
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ mut This {
|
||||
type Type = This::Type;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
This::to_sim_value(self)
|
||||
}
|
||||
|
|
@ -555,8 +818,6 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'
|
|||
}
|
||||
|
||||
impl<This: ?Sized + ToSimValue> ToSimValue for Arc<This> {
|
||||
type Type = This::Type;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
This::arc_to_sim_value(self)
|
||||
}
|
||||
|
|
@ -577,7 +838,6 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for Ar
|
|||
impl<This: ?Sized + ToSimValue + Send + Sync + 'static> ToSimValue
|
||||
for crate::intern::Interned<This>
|
||||
{
|
||||
type Type = This::Type;
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
This::to_sim_value(self)
|
||||
}
|
||||
|
|
@ -592,8 +852,6 @@ impl<This: ?Sized + ToSimValueWithType<T> + Send + Sync + 'static, T: Type> ToSi
|
|||
}
|
||||
|
||||
impl<This: ToSimValue> ToSimValue for Box<This> {
|
||||
type Type = This::Type;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
This::to_sim_value(self)
|
||||
}
|
||||
|
|
@ -636,8 +894,6 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for [
|
|||
}
|
||||
|
||||
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for [Element] {
|
||||
type Type = Array<Element::Type>;
|
||||
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
||||
|
|
@ -673,8 +929,6 @@ impl<Element: ToSimValue<Type: StaticType>, const N: usize> ToSimValue for [Elem
|
|||
where
|
||||
ConstUsize<N>: KnownSize,
|
||||
{
|
||||
type Type = Array<Element::Type, N>;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_array_elements(StaticType::TYPE, self)
|
||||
}
|
||||
|
|
@ -728,8 +982,6 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for V
|
|||
}
|
||||
|
||||
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Vec<Element> {
|
||||
type Type = Array<Element::Type>;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
||||
}
|
||||
|
|
@ -770,8 +1022,6 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for B
|
|||
}
|
||||
|
||||
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Box<[Element]> {
|
||||
type Type = Array<Element::Type>;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
|
||||
}
|
||||
|
|
@ -801,11 +1051,10 @@ impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalTyp
|
|||
}
|
||||
|
||||
impl<T: Type> ToSimValue for Expr<T> {
|
||||
type Type = T;
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_bitslice(
|
||||
Expr::ty(*self),
|
||||
self.ty(),
|
||||
&crate::expr::ToLiteralBits::to_literal_bits(self)
|
||||
.expect("must be a literal expression"),
|
||||
)
|
||||
|
|
@ -825,8 +1074,6 @@ macro_rules! impl_to_sim_value_for_bool_like {
|
|||
}
|
||||
|
||||
impl ToSimValue for bool {
|
||||
type Type = Bool;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_value(Bool, *self)
|
||||
}
|
||||
|
|
@ -864,10 +1111,8 @@ impl ToSimValueWithType<CanonicalType> for bool {
|
|||
}
|
||||
|
||||
macro_rules! impl_to_sim_value_for_primitive_int {
|
||||
($prim:ident) => {
|
||||
($prim:ty) => {
|
||||
impl ToSimValue for $prim {
|
||||
type Type = <$prim as ToExpr>::Type;
|
||||
|
||||
#[track_caller]
|
||||
fn to_sim_value(
|
||||
&self,
|
||||
|
|
@ -878,15 +1123,15 @@ macro_rules! impl_to_sim_value_for_primitive_int {
|
|||
|
||||
forward_to_sim_value_with_type!([] $prim);
|
||||
|
||||
impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim {
|
||||
impl ToSimValueWithType<<<$prim as ValueType>::Type as IntType>::Dyn> for $prim {
|
||||
#[track_caller]
|
||||
fn to_sim_value_with_type(
|
||||
&self,
|
||||
ty: <<$prim as ToExpr>::Type as IntType>::Dyn,
|
||||
) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> {
|
||||
ty: <<$prim as ValueType>::Type as IntType>::Dyn,
|
||||
) -> SimValue<<<$prim as ValueType>::Type as IntType>::Dyn> {
|
||||
SimValue::from_value(
|
||||
ty,
|
||||
<<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(),
|
||||
<<$prim as ValueType>::Type as Type>::SimValue::from(*self).as_dyn_int(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -894,7 +1139,7 @@ macro_rules! impl_to_sim_value_for_primitive_int {
|
|||
impl ToSimValueWithType<CanonicalType> for $prim {
|
||||
#[track_caller]
|
||||
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty);
|
||||
let ty: <<$prim as ValueType>::Type as IntType>::Dyn = Type::from_canonical(ty);
|
||||
SimValue::into_canonical(self.to_sim_value_with_type(ty))
|
||||
}
|
||||
}
|
||||
|
|
@ -913,12 +1158,22 @@ impl_to_sim_value_for_primitive_int!(i32);
|
|||
impl_to_sim_value_for_primitive_int!(i64);
|
||||
impl_to_sim_value_for_primitive_int!(i128);
|
||||
impl_to_sim_value_for_primitive_int!(isize);
|
||||
impl_to_sim_value_for_primitive_int!(NonZero<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 {
|
||||
($IntValue:ident, $Int:ident, $IntType:ident) => {
|
||||
impl<Width: Size> ToSimValue for $IntValue<Width> {
|
||||
type Type = $IntType<Width>;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_value(self.ty(), self.clone())
|
||||
}
|
||||
|
|
@ -1279,8 +1534,6 @@ impl<T: SimOnlyValueTrait> ToSimValueWithType<SimOnly<T>> for SimOnlyValue<T> {
|
|||
}
|
||||
|
||||
impl ToSimValue for DynSimOnlyValue {
|
||||
type Type = DynSimOnly;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_value(self.ty(), self.clone())
|
||||
}
|
||||
|
|
@ -1290,15 +1543,58 @@ impl ToSimValue for DynSimOnlyValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: SimOnlyValueTrait> ToSimValue for SimOnlyValue<T> {
|
||||
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> {
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
SimValue::from_value(Default::default(), self.clone())
|
||||
SimValue::from_value(self.ty(), self.clone())
|
||||
}
|
||||
|
||||
fn into_sim_value(self) -> SimValue<Self::Type> {
|
||||
SimValue::from_value(Default::default(), self)
|
||||
SimValue::from_value(self.ty(), self)
|
||||
}
|
||||
}
|
||||
|
||||
impl HdlPartialEqImpl<Self> for DynSimOnly {
|
||||
#[track_caller]
|
||||
fn cmp_value_eq(
|
||||
_lhs: Self,
|
||||
lhs_value: Cow<'_, Self::SimValue>,
|
||||
_rhs: Self,
|
||||
rhs_value: Cow<'_, Self::SimValue>,
|
||||
) -> bool {
|
||||
*lhs_value == *rhs_value
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_expr_eq(_lhs: Expr<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>
|
||||
{
|
||||
#[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,6 +3,7 @@
|
|||
|
||||
//! `unsafe` parts of [`DynSimOnlyValue`]
|
||||
|
||||
use crate::expr::{ValueType, value_category::ValueCategoryValue};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::{
|
||||
any::{self, TypeId},
|
||||
|
|
@ -206,9 +207,25 @@ impl<T: SimOnlyValueTrait> Default for SimOnly<T> {
|
|||
}
|
||||
|
||||
/// a value that can only be used in a Fayalite simulation, it can't be converted to FIRRTL
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Default, PartialOrd, Ord)]
|
||||
#[derive(Clone, Eq, Hash, Default, Ord)]
|
||||
pub struct SimOnlyValue<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> {
|
||||
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
|
||||
|
|
@ -279,10 +296,16 @@ impl<T: SimOnlyValueTrait> From<SimOnlyValue<T>> for DynSimOnlyValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl DynSimOnlyValue {
|
||||
pub fn ty(&self) -> DynSimOnly {
|
||||
impl ValueType for DynSimOnlyValue {
|
||||
type Type = DynSimOnly;
|
||||
type ValueCategory = ValueCategoryValue;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.0.ty()
|
||||
}
|
||||
}
|
||||
|
||||
impl DynSimOnlyValue {
|
||||
pub fn type_id(&self) -> TypeId {
|
||||
self.0.type_id_dyn()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ use crate::{
|
|||
expr::Flow,
|
||||
int::UInt,
|
||||
intern::{Intern, Interned},
|
||||
prelude::PhantomConst,
|
||||
sim::{
|
||||
TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl,
|
||||
TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceInstance,
|
||||
TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceModule,
|
||||
TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSimOnly,
|
||||
TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls,
|
||||
TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt, TraceScalar, TraceScalarId,
|
||||
TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire, TraceWriter,
|
||||
TraceWriterDecls,
|
||||
time::{SimDuration, SimInstant},
|
||||
value::DynSimOnlyValue,
|
||||
},
|
||||
|
|
@ -283,6 +285,7 @@ impl WriteTrace for TraceScalar {
|
|||
Self::Clock(v) => v.write_trace(writer, arg),
|
||||
Self::SyncReset(v) => v.write_trace(writer, arg),
|
||||
Self::AsyncReset(v) => v.write_trace(writer, arg),
|
||||
Self::PhantomConst(v) => v.write_trace(writer, arg),
|
||||
Self::SimOnly(v) => v.write_trace(writer, arg),
|
||||
}
|
||||
}
|
||||
|
|
@ -549,6 +552,33 @@ impl WriteTrace for TraceAsyncReset {
|
|||
}
|
||||
}
|
||||
|
||||
impl WriteTrace for TracePhantomConst {
|
||||
fn write_trace<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,
|
||||
MemoryElementPartBody::Scalar,
|
||||
writer,
|
||||
"string",
|
||||
1,
|
||||
location,
|
||||
scope.new_identifier(name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteTrace for TraceSimOnly {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let ArgInType {
|
||||
|
|
@ -1091,6 +1121,16 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
|
|||
write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize())
|
||||
}
|
||||
|
||||
fn set_signal_phantom_const(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
ty: PhantomConst,
|
||||
) -> Result<(), Self::Error> {
|
||||
// avoid multi-line strings because GTKWave can't display them properly:
|
||||
// https://github.com/gtkwave/gtkwave/issues/460
|
||||
write_string_value_change(&mut self.writer, format_args!("{ty:?}"), id.as_usize())
|
||||
}
|
||||
|
||||
fn set_signal_sim_only_value(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
|
|
|
|||
|
|
@ -1,25 +1,54 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
cli::{FormalArgs, FormalMode, FormalOutput, RunPhase},
|
||||
build::{
|
||||
BaseJobArgs, BaseJobKind, GlobalParams, JobArgsAndDependencies, JobKindAndArgs, JobParams,
|
||||
NoArgs, RunBuild,
|
||||
external::{ExternalCommandArgs, ExternalCommandJobKind},
|
||||
firrtl::{FirrtlArgs, FirrtlJobKind},
|
||||
formal::{Formal, FormalAdditionalArgs, FormalArgs, WriteSbyFileJobKind},
|
||||
verilog::{UnadjustedVerilogArgs, VerilogJobArgs, VerilogJobKind},
|
||||
},
|
||||
bundle::BundleType,
|
||||
firrtl::ExportOptions,
|
||||
module::Module,
|
||||
util::HashMap,
|
||||
};
|
||||
use clap::Parser;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt::Write,
|
||||
fmt::{self, Write},
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
sync::{Mutex, OnceLock},
|
||||
};
|
||||
|
||||
fn assert_formal_helper() -> FormalArgs {
|
||||
static FORMAL_ARGS: OnceLock<FormalArgs> = OnceLock::new();
|
||||
// ensure we only run parsing once, so errors from env vars don't produce overlapping output if we're called on multiple threads
|
||||
FORMAL_ARGS
|
||||
.get_or_init(|| FormalArgs::parse_from(["fayalite::testing::assert_formal"]))
|
||||
.clone()
|
||||
#[derive(
|
||||
clap::ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default, Deserialize, Serialize,
|
||||
)]
|
||||
#[non_exhaustive]
|
||||
pub enum FormalMode {
|
||||
#[default]
|
||||
BMC,
|
||||
Prove,
|
||||
Live,
|
||||
Cover,
|
||||
}
|
||||
|
||||
impl FormalMode {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
FormalMode::BMC => "bmc",
|
||||
FormalMode::Prove => "prove",
|
||||
FormalMode::Live => "live",
|
||||
FormalMode::Cover => "cover",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FormalMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -97,26 +126,99 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf {
|
|||
.join(dir)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn assert_formal<M>(
|
||||
test_name: impl std::fmt::Display,
|
||||
module: M,
|
||||
mode: FormalMode,
|
||||
depth: u64,
|
||||
fn make_assert_formal_args(
|
||||
test_name: &dyn std::fmt::Display,
|
||||
formal_mode: FormalMode,
|
||||
formal_depth: u64,
|
||||
solver: Option<&str>,
|
||||
export_options: ExportOptions,
|
||||
) where
|
||||
FormalArgs: RunPhase<M, Output = FormalOutput>,
|
||||
{
|
||||
let mut args = assert_formal_helper();
|
||||
args.verilog.firrtl.base.redirect_output_for_rust_test = true;
|
||||
args.verilog.firrtl.base.output = Some(get_assert_formal_target_path(&test_name));
|
||||
args.verilog.firrtl.export_options = export_options;
|
||||
args.verilog.debug = true;
|
||||
args.mode = mode;
|
||||
args.depth = depth;
|
||||
if let Some(solver) = solver {
|
||||
args.solver = solver.into();
|
||||
) -> 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 })
|
||||
}
|
||||
args.run(module).expect("testing::assert_formal() failed");
|
||||
|
||||
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]
|
||||
pub fn 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,
|
||||
) {
|
||||
try_assert_formal(
|
||||
test_name,
|
||||
module,
|
||||
formal_mode,
|
||||
formal_depth,
|
||||
solver,
|
||||
export_options,
|
||||
)
|
||||
.expect("testing::assert_formal() failed");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
intern::{Intern, Interned},
|
||||
phantom_const::PhantomConst,
|
||||
reset::{AsyncReset, Reset, SyncReset},
|
||||
sim::value::{DynSimOnlyValue, DynSimOnly, SimValue, ToSimValueWithType},
|
||||
sim::value::{DynSimOnly, DynSimOnlyValue, SimValue, ToSimValueWithType},
|
||||
source_location::SourceLocation,
|
||||
util::{ConstUsize, slice_range, try_slice_range},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ impl From<SerdeCanonicalType> for CanonicalType {
|
|||
SerdeCanonicalType::Reset => Self::Reset(Reset),
|
||||
SerdeCanonicalType::Clock => Self::Clock(Clock),
|
||||
SerdeCanonicalType::PhantomConst(value) => {
|
||||
Self::PhantomConst(PhantomConst::new(value.0))
|
||||
Self::PhantomConst(PhantomConst::new_interned(value.0))
|
||||
}
|
||||
SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,12 +33,15 @@ pub use const_cmp::{
|
|||
#[doc(inline)]
|
||||
pub use scoped_ref::ScopedRef;
|
||||
|
||||
pub(crate) use misc::chain;
|
||||
#[doc(inline)]
|
||||
pub use misc::{
|
||||
BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, interned_bit,
|
||||
iter_eq_by, slice_range, try_slice_range,
|
||||
BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter,
|
||||
SerdeJsonEscapeIf, SerdeJsonEscapeIfFormatter, SerdeJsonEscapeIfTest,
|
||||
SerdeJsonEscapeIfTestResult, interned_bit, iter_eq_by, os_str_strip_prefix,
|
||||
os_str_strip_suffix, serialize_to_json_ascii, serialize_to_json_ascii_pretty,
|
||||
serialize_to_json_ascii_pretty_with_indent, slice_range, try_slice_range,
|
||||
};
|
||||
pub(crate) use misc::{InternedStrCompareAsStr, chain};
|
||||
|
||||
pub mod job_server;
|
||||
pub mod prefix_sum;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,36 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use ctor::ctor;
|
||||
use jobslot::{Acquired, Client};
|
||||
use ctor::{ctor, dtor};
|
||||
use jobslot::Client;
|
||||
use std::{
|
||||
ffi::OsString,
|
||||
mem,
|
||||
io, mem,
|
||||
num::NonZeroUsize,
|
||||
sync::{Condvar, Mutex, Once, OnceLock},
|
||||
thread::spawn,
|
||||
sync::{Mutex, MutexGuard},
|
||||
};
|
||||
|
||||
fn get_or_make_client() -> &'static Client {
|
||||
#[ctor]
|
||||
static CLIENT: OnceLock<Client> = unsafe {
|
||||
match Client::from_env() {
|
||||
Some(client) => OnceLock::from(client),
|
||||
None => OnceLock::new(),
|
||||
}
|
||||
};
|
||||
static CLIENT: Mutex<Option<Option<Client>>> = unsafe { Mutex::new(Some(Client::from_env())) };
|
||||
|
||||
CLIENT.get_or_init(|| {
|
||||
#[dtor]
|
||||
fn drop_client() {
|
||||
drop(
|
||||
match CLIENT.lock() {
|
||||
Ok(v) => v,
|
||||
Err(e) => e.into_inner(),
|
||||
}
|
||||
.take(),
|
||||
);
|
||||
}
|
||||
|
||||
fn get_or_make_client() -> Client {
|
||||
CLIENT
|
||||
.lock()
|
||||
.expect("shouldn't have panicked")
|
||||
.as_mut()
|
||||
.expect("shutting down")
|
||||
.get_or_insert_with(|| {
|
||||
let mut available_parallelism = None;
|
||||
let mut args = std::env::args_os().skip(1);
|
||||
while let Some(arg) = args.next() {
|
||||
|
|
@ -52,141 +62,95 @@ fn get_or_make_client() -> &'static Client {
|
|||
} else {
|
||||
NonZeroUsize::new(1).unwrap()
|
||||
};
|
||||
Client::new_with_fifo(available_parallelism.get() - 1).expect("failed to create job server")
|
||||
Client::new_with_fifo(available_parallelism.get() - 1)
|
||||
.expect("failed to create job server")
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
struct State {
|
||||
obtained_count: usize,
|
||||
waiting_count: usize,
|
||||
available: Vec<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 {
|
||||
obtained_count: 0,
|
||||
waiting_count: 0,
|
||||
available: Vec::new(),
|
||||
implicit_available: true,
|
||||
});
|
||||
static COND_VAR: Condvar = Condvar::new();
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AcquiredJobInner {
|
||||
FromJobServer(Acquired),
|
||||
ImplicitJob,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AcquiredJob {
|
||||
job: AcquiredJobInner,
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl AcquiredJob {
|
||||
fn start_acquire_thread() {
|
||||
static STARTED_THREAD: Once = Once::new();
|
||||
STARTED_THREAD.call_once(|| {
|
||||
spawn(|| {
|
||||
let mut acquired = None;
|
||||
pub fn acquire() -> io::Result<Self> {
|
||||
let client = get_or_make_client();
|
||||
struct Waiting {}
|
||||
|
||||
impl Waiting {
|
||||
fn done(self) -> MutexGuard<'static, State> {
|
||||
mem::forget(self);
|
||||
let mut state = STATE.lock().unwrap();
|
||||
loop {
|
||||
state = if state.additional_waiting() == 0 {
|
||||
if acquired.is_some() {
|
||||
drop(state);
|
||||
drop(acquired.take()); // drop Acquired outside of lock
|
||||
STATE.lock().unwrap()
|
||||
} else {
|
||||
COND_VAR.wait(state).unwrap()
|
||||
}
|
||||
} else if acquired.is_some() {
|
||||
// allocate space before moving Acquired to ensure we
|
||||
// drop Acquired outside of the lock on panic
|
||||
state.available.reserve(1);
|
||||
state.available.push(acquired.take().unwrap());
|
||||
COND_VAR.notify_all();
|
||||
state.waiting_count -= 1;
|
||||
state
|
||||
} else {
|
||||
drop(state);
|
||||
acquired = Some(
|
||||
client
|
||||
.acquire()
|
||||
.expect("can't acquire token from job server"),
|
||||
);
|
||||
STATE.lock().unwrap()
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
fn acquire_inner(block: bool) -> Option<Self> {
|
||||
Self::start_acquire_thread();
|
||||
impl Drop for Waiting {
|
||||
fn drop(&mut self) {
|
||||
STATE.lock().unwrap().waiting_count -= 1;
|
||||
}
|
||||
}
|
||||
let mut state = STATE.lock().unwrap();
|
||||
loop {
|
||||
if let Some(acquired) = state.available.pop() {
|
||||
return Some(Self {
|
||||
job: AcquiredJobInner::FromJobServer(acquired),
|
||||
});
|
||||
}
|
||||
if state.implicit_available {
|
||||
state.implicit_available = false;
|
||||
return Some(Self {
|
||||
job: AcquiredJobInner::ImplicitJob,
|
||||
});
|
||||
}
|
||||
if !block {
|
||||
return None;
|
||||
if state.obtained_count == 0 && state.waiting_count == 0 {
|
||||
state.obtained_count = 1; // get implicit token
|
||||
return Ok(Self { client });
|
||||
}
|
||||
state.waiting_count += 1;
|
||||
state = COND_VAR.wait(state).unwrap();
|
||||
state.waiting_count -= 1;
|
||||
}
|
||||
}
|
||||
pub fn try_acquire() -> Option<Self> {
|
||||
Self::acquire_inner(false)
|
||||
}
|
||||
pub fn acquire() -> Self {
|
||||
Self::acquire_inner(true).expect("failed to acquire token")
|
||||
drop(state);
|
||||
let waiting = Waiting {};
|
||||
client.acquire_raw()?;
|
||||
state = waiting.done();
|
||||
state.obtained_count = state
|
||||
.obtained_count
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "obtained_count overflowed"))?;
|
||||
drop(state);
|
||||
Ok(Self { client })
|
||||
}
|
||||
pub fn run_command<R>(
|
||||
&mut self,
|
||||
cmd: std::process::Command,
|
||||
f: impl FnOnce(&mut std::process::Command) -> std::io::Result<R>,
|
||||
) -> std::io::Result<R> {
|
||||
get_or_make_client().configure_make_and_run_with_fifo(cmd, f)
|
||||
self.client.configure_make_and_run_with_fifo(cmd, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AcquiredJob {
|
||||
fn drop(&mut self) {
|
||||
let mut state = STATE.lock().unwrap();
|
||||
match &self.job {
|
||||
AcquiredJobInner::FromJobServer(_) => {
|
||||
if state.waiting_count > state.available.len() + state.implicit_available as usize {
|
||||
// allocate space before moving Acquired to ensure we
|
||||
// drop Acquired outside of the lock on panic
|
||||
state.available.reserve(1);
|
||||
let AcquiredJobInner::FromJobServer(acquired) =
|
||||
mem::replace(&mut self.job, AcquiredJobInner::ImplicitJob)
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
state.available.push(acquired);
|
||||
COND_VAR.notify_all();
|
||||
match &mut *state {
|
||||
State {
|
||||
obtained_count: 0, ..
|
||||
} => unreachable!(),
|
||||
State {
|
||||
obtained_count: obtained_count @ 1,
|
||||
waiting_count,
|
||||
} => {
|
||||
*obtained_count = 0; // drop implicit token
|
||||
let any_waiting = *waiting_count != 0;
|
||||
drop(state);
|
||||
if any_waiting {
|
||||
// we have the implicit token, but some other thread is trying to acquire a token,
|
||||
// release the implicit token so they can acquire it.
|
||||
let _ = self.client.release_raw(); // we're in drop, just ignore errors since we at least tried
|
||||
}
|
||||
}
|
||||
AcquiredJobInner::ImplicitJob => {
|
||||
state.implicit_available = true;
|
||||
if state.waiting_count > state.available.len() {
|
||||
COND_VAR.notify_all();
|
||||
}
|
||||
State { obtained_count, .. } => {
|
||||
*obtained_count = obtained_count.saturating_sub(1);
|
||||
drop(state);
|
||||
let _ = self.client.release_raw(); // we're in drop, just ignore errors since we at least tried
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ use crate::intern::{Intern, Interned};
|
|||
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
ffi::OsStr,
|
||||
fmt::{self, Debug, Write},
|
||||
io,
|
||||
ops::{Bound, Range, RangeBounds},
|
||||
rc::Rc,
|
||||
sync::{Arc, OnceLock},
|
||||
|
|
@ -243,3 +245,370 @@ pub fn try_slice_range<R: RangeBounds<usize>>(range: R, size: usize) -> Option<R
|
|||
pub fn slice_range<R: RangeBounds<usize>>(range: R, size: usize) -> Range<usize> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ impl<T: Type> ReadyValid<T> {
|
|||
#[hdl]
|
||||
pub fn firing_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> {
|
||||
let expr = expr.to_expr();
|
||||
let option_ty = Expr::ty(expr).data;
|
||||
let option_ty = expr.ty().data;
|
||||
#[hdl]
|
||||
let firing_data = wire(option_ty);
|
||||
connect(firing_data, option_ty.HdlNone());
|
||||
|
|
@ -42,7 +42,7 @@ impl<T: Type> ReadyValid<T> {
|
|||
) -> Expr<ReadyValid<R>> {
|
||||
let data = HdlOption::map(expr.data, f);
|
||||
#[hdl]
|
||||
let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]);
|
||||
let mapped = wire(ReadyValid[data.ty().HdlSome]);
|
||||
connect(mapped.data, data);
|
||||
connect(expr.ready, mapped.ready);
|
||||
mapped
|
||||
|
|
@ -81,7 +81,7 @@ pub fn queue<T: Type>(
|
|||
let count: UInt = m.output(count_ty);
|
||||
|
||||
#[hdl]
|
||||
let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
||||
let inp_index_reg: UInt = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
||||
#[hdl]
|
||||
let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
||||
#[hdl]
|
||||
|
|
@ -212,9 +212,7 @@ pub fn queue<T: Type>(
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
cli::FormalMode, firrtl::ExportOptions,
|
||||
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
|
||||
ty::StaticType,
|
||||
firrtl::ExportOptions, module::transform::simplify_enums::SimplifyEnumsKind, ty::StaticType,
|
||||
};
|
||||
use std::num::NonZero;
|
||||
|
||||
|
|
|
|||
12
crates/fayalite/src/vendor.rs
Normal file
12
crates/fayalite/src/vendor.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
pub mod xilinx;
|
||||
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<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
Normal file
207
crates/fayalite/src/vendor/xilinx.rs
vendored
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
annotations::make_annotation_enum,
|
||||
build::{GlobalParams, ToArgs, WriteArgs},
|
||||
intern::Interned,
|
||||
prelude::{DynPlatform, Platform},
|
||||
};
|
||||
use clap::ValueEnum;
|
||||
use ordered_float::NotNan;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
pub mod arty_a7;
|
||||
pub mod primitives;
|
||||
pub mod yosys_nextpnr_prjxray;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct XdcIOStandardAnnotation {
|
||||
pub value: Interned<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
Normal file
404
crates/fayalite/src/vendor/xilinx/arty_a7.rs
vendored
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
intern::{Intern, Interned},
|
||||
module::{instance_with_loc, reg_builder_with_loc, wire_with_loc},
|
||||
platform::{
|
||||
DynPlatform, Peripheral, PeripheralRef, Peripherals, PeripheralsBuilderFactory,
|
||||
PeripheralsBuilderFinished, Platform, PlatformAspectSet,
|
||||
peripherals::{ClockInput, Led, RgbLed, Uart},
|
||||
},
|
||||
prelude::*,
|
||||
vendor::xilinx::{
|
||||
Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation,
|
||||
primitives,
|
||||
},
|
||||
};
|
||||
use ordered_float::NotNan;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
macro_rules! arty_a7_platform {
|
||||
(
|
||||
$vis:vis enum $ArtyA7Platform:ident {
|
||||
$(#[name = $name:literal, device = $device:ident]
|
||||
$Variant:ident,)*
|
||||
}
|
||||
) => {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
$vis enum $ArtyA7Platform {
|
||||
$($Variant,)*
|
||||
}
|
||||
|
||||
impl $ArtyA7Platform {
|
||||
$vis const VARIANTS: &'static [Self] = &[$(Self::$Variant,)*];
|
||||
$vis fn device(self) -> Device {
|
||||
match self {
|
||||
$(Self::$Variant => Device::$device,)*
|
||||
}
|
||||
}
|
||||
$vis const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
$(Self::$Variant => $name,)*
|
||||
}
|
||||
}
|
||||
fn get_aspects(self) -> &'static PlatformAspectSet {
|
||||
match self {
|
||||
$(Self::$Variant => {
|
||||
static ASPECTS_SET: OnceLock<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
Normal file
50
crates/fayalite/src/vendor/xilinx/primitives.rs
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[hdl_module(extern)]
|
||||
pub fn IBUF() {
|
||||
m.verilog_name("IBUF");
|
||||
#[hdl]
|
||||
let O: Bool = m.output();
|
||||
#[hdl]
|
||||
let I: Bool = m.input();
|
||||
}
|
||||
|
||||
#[hdl_module(extern)]
|
||||
pub fn OBUFT() {
|
||||
m.verilog_name("OBUFT");
|
||||
#[hdl]
|
||||
let O: Bool = m.output();
|
||||
#[hdl]
|
||||
let I: Bool = m.input();
|
||||
#[hdl]
|
||||
let T: Bool = m.input();
|
||||
}
|
||||
|
||||
#[hdl_module(extern)]
|
||||
pub fn BUFGCE() {
|
||||
m.verilog_name("BUFGCE");
|
||||
#[hdl]
|
||||
let O: Clock = m.output();
|
||||
#[hdl]
|
||||
let CE: Bool = m.input();
|
||||
#[hdl]
|
||||
let I: Clock = m.input();
|
||||
}
|
||||
|
||||
#[hdl_module(extern)]
|
||||
pub fn STARTUPE2_default_inputs() {
|
||||
m.verilog_name("STARTUPE2");
|
||||
#[hdl]
|
||||
let CFGCLK: Clock = m.output();
|
||||
#[hdl]
|
||||
let CFGMCLK: Clock = m.output();
|
||||
#[hdl]
|
||||
let EOS: Bool = m.output();
|
||||
#[hdl]
|
||||
let PREQ: Bool = m.output();
|
||||
}
|
||||
1043
crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs
vendored
Normal file
1043
crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::{Expr, Flow, ToExpr},
|
||||
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr},
|
||||
intern::Interned,
|
||||
module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire},
|
||||
source_location::SourceLocation,
|
||||
|
|
@ -16,7 +16,16 @@ pub struct Wire<T: Type> {
|
|||
ty: T,
|
||||
}
|
||||
|
||||
impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
|
||||
impl<T: Type> ValueType 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 {
|
||||
write!(f, "Wire({:?}: ", self.name)?;
|
||||
self.ty.fmt(f)?;
|
||||
|
|
@ -49,9 +58,6 @@ impl<T: Type> Wire<T> {
|
|||
ty: T::from_canonical(ty),
|
||||
}
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
}
|
||||
pub fn new_unchecked(
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
|
|
|
|||
|
|
@ -2,19 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
//! Formal tests in Fayalite
|
||||
|
||||
use fayalite::{
|
||||
cli::FormalMode,
|
||||
clock::{Clock, ClockDomain},
|
||||
expr::{CastTo, HdlPartialEq},
|
||||
firrtl::ExportOptions,
|
||||
formal::{any_const, any_seq, formal_reset, hdl_assert, hdl_assume},
|
||||
hdl, hdl_module,
|
||||
int::{Bool, DynSize, Size, UInt, UIntType},
|
||||
module::{connect, connect_any, instance, memory, reg_builder, wire},
|
||||
reset::ToReset,
|
||||
testing::assert_formal,
|
||||
ty::StaticType,
|
||||
};
|
||||
use fayalite::prelude::*;
|
||||
|
||||
/// Test hidden state
|
||||
///
|
||||
|
|
@ -119,7 +107,7 @@ mod hidden_state {
|
|||
FormalMode::Prove,
|
||||
16,
|
||||
None,
|
||||
ExportOptions::default(),
|
||||
Default::default(),
|
||||
);
|
||||
// here a couple of cycles is enough
|
||||
assert_formal(
|
||||
|
|
@ -128,7 +116,7 @@ mod hidden_state {
|
|||
FormalMode::Prove,
|
||||
2,
|
||||
None,
|
||||
ExportOptions::default(),
|
||||
Default::default(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -242,7 +230,7 @@ mod memory {
|
|||
#[hdl]
|
||||
let wr: WritePort<DynSize> = wire(WritePort[n]);
|
||||
connect(wr.addr, any_seq(UInt[n]));
|
||||
connect(wr.data, any_seq(UInt::<8>::TYPE));
|
||||
connect(wr.data, any_seq(UInt::<8>::new_static()));
|
||||
connect(wr.en, any_seq(Bool));
|
||||
#[hdl]
|
||||
let dut = instance(example_sram(n));
|
||||
|
|
@ -289,7 +277,7 @@ mod memory {
|
|||
FormalMode::Prove,
|
||||
2,
|
||||
None,
|
||||
ExportOptions::default(),
|
||||
Default::default(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use fayalite::{
|
|||
bundle::BundleType,
|
||||
enum_::EnumType,
|
||||
int::{BoolOrIntType, IntType},
|
||||
phantom_const::PhantomConst,
|
||||
prelude::*,
|
||||
ty::StaticType,
|
||||
};
|
||||
|
|
@ -197,3 +196,51 @@ check_bounds!(CheckBoundsTTT2<#[a, Type] A: BundleType +, #[b, Type] B: Type +,
|
|||
check_bounds!(CheckBoundsTTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MyPhantomConstInner {
|
||||
pub a: usize,
|
||||
pub b: UInt,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated, get(|v| v.a))]
|
||||
pub type GetA<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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use fayalite::{
|
|||
int::{UIntInRange, UIntInRangeInclusive},
|
||||
intern::Intern,
|
||||
module::transform::simplify_enums::SimplifyEnumsKind,
|
||||
platform::PlatformIOBuilder,
|
||||
prelude::*,
|
||||
reset::ResetType,
|
||||
ty::StaticType,
|
||||
|
|
@ -214,7 +215,7 @@ where
|
|||
let o: Array<T, N> = m.output();
|
||||
let bytes = v.to_string().as_bytes().to_expr();
|
||||
#[hdl]
|
||||
let o2: Array<UInt<8>> = m.output(Expr::ty(bytes));
|
||||
let o2: Array<UInt<8>> = m.output(bytes.ty());
|
||||
connect(
|
||||
o,
|
||||
#[hdl]
|
||||
|
|
@ -1175,7 +1176,7 @@ pub fn check_memory_init() {
|
|||
let waddr2: UInt<4> = m.input();
|
||||
#[hdl]
|
||||
let wdata2: UInt<31> = m.input();
|
||||
let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static()));
|
||||
let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static::<UInt<31>>()));
|
||||
#[hdl]
|
||||
let mut mem2 = memory_with_init(mem_init2);
|
||||
let read_port2 = mem2.new_read_port();
|
||||
|
|
@ -1783,7 +1784,7 @@ pub fn check_memory_of_bundle_of_arrays() {
|
|||
[(i * i).wrapping_mul(2), (i * i).wrapping_mul(3)],
|
||||
[(i * i).wrapping_mul(i), i * 2],
|
||||
],
|
||||
i.cast_to_static(),
|
||||
i.cast_to_static::<UInt<2>>(),
|
||||
)
|
||||
}));
|
||||
#[hdl]
|
||||
|
|
@ -1953,9 +1954,9 @@ pub fn check_memory_of_array_of_bundle() {
|
|||
let clk: Clock = m.input();
|
||||
let mem_init = Vec::from_iter((0..0x10u8).map(|i| {
|
||||
[
|
||||
(i, i.cast_to_static()),
|
||||
((i * i), (i / 2).cast_to_static()),
|
||||
((i * i).wrapping_mul(i), (i / 4).cast_to_static()),
|
||||
(i, i.cast_to_static::<SInt<1>>()),
|
||||
((i * i), (i / 2).cast_to_static::<SInt<1>>()),
|
||||
((i * i).wrapping_mul(i), (i / 4).cast_to_static::<SInt<1>>()),
|
||||
]
|
||||
}));
|
||||
#[hdl]
|
||||
|
|
@ -2268,7 +2269,8 @@ pub fn check_memory_of_bundle() {
|
|||
let wmask: (Bool, Bool) = m.input();
|
||||
#[hdl]
|
||||
let clk: Clock = m.input();
|
||||
let mem_init = Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static())));
|
||||
let mem_init =
|
||||
Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static::<SInt<1>>())));
|
||||
#[hdl]
|
||||
let mut mem = memory_with_init(mem_init);
|
||||
let read_port = mem.new_read_port();
|
||||
|
|
@ -4631,3 +4633,55 @@ circuit check_uint_in_range:
|
|||
",
|
||||
};
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn check_platform_io(platform_io_builder: PlatformIOBuilder<'_>) {
|
||||
#[hdl]
|
||||
let io = m.add_platform_io(platform_io_builder);
|
||||
}
|
||||
|
||||
#[cfg(todo)]
|
||||
#[test]
|
||||
fn test_platform_io() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let m = check_platform_io(todo!());
|
||||
dbg!(m);
|
||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
||||
assert_export_firrtl! {
|
||||
m =>
|
||||
"/test/check_platform_io.fir": r"FIRRTL version 3.2.0
|
||||
circuit check_platform_io:
|
||||
type Ty0 = {value: UInt<0>, range: {}}
|
||||
type Ty1 = {value: UInt<1>, range: {}}
|
||||
type Ty2 = {value: UInt<2>, range: {}}
|
||||
type Ty3 = {value: UInt<2>, range: {}}
|
||||
type Ty4 = {value: UInt<3>, range: {}}
|
||||
type Ty5 = {value: UInt<3>, range: {}}
|
||||
type Ty6 = {value: UInt<4>, range: {}}
|
||||
type Ty7 = {value: UInt<0>, range: {}}
|
||||
type Ty8 = {value: UInt<1>, range: {}}
|
||||
type Ty9 = {value: UInt<2>, range: {}}
|
||||
type Ty10 = {value: UInt<2>, range: {}}
|
||||
type Ty11 = {value: UInt<3>, range: {}}
|
||||
type Ty12 = {value: UInt<3>, range: {}}
|
||||
type Ty13 = {value: UInt<4>, range: {}}
|
||||
type Ty14 = {value: UInt<4>, range: {}}
|
||||
module check_platform_io: @[module-XXXXXXXXXX.rs 1:1]
|
||||
input i_0_to_1: Ty0 @[module-XXXXXXXXXX.rs 2:1]
|
||||
input i_0_to_2: Ty1 @[module-XXXXXXXXXX.rs 3:1]
|
||||
input i_0_to_3: Ty2 @[module-XXXXXXXXXX.rs 4:1]
|
||||
input i_0_to_4: Ty3 @[module-XXXXXXXXXX.rs 5:1]
|
||||
input i_0_to_7: Ty4 @[module-XXXXXXXXXX.rs 6:1]
|
||||
input i_0_to_8: Ty5 @[module-XXXXXXXXXX.rs 7:1]
|
||||
input i_0_to_9: Ty6 @[module-XXXXXXXXXX.rs 8:1]
|
||||
input i_0_through_0: Ty7 @[module-XXXXXXXXXX.rs 9:1]
|
||||
input i_0_through_1: Ty8 @[module-XXXXXXXXXX.rs 10:1]
|
||||
input i_0_through_2: Ty9 @[module-XXXXXXXXXX.rs 11:1]
|
||||
input i_0_through_3: Ty10 @[module-XXXXXXXXXX.rs 12:1]
|
||||
input i_0_through_4: Ty11 @[module-XXXXXXXXXX.rs 13:1]
|
||||
input i_0_through_7: Ty12 @[module-XXXXXXXXXX.rs 14:1]
|
||||
input i_0_through_8: Ty13 @[module-XXXXXXXXXX.rs 15:1]
|
||||
input i_0_through_9: Ty14 @[module-XXXXXXXXXX.rs 16:1]
|
||||
",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use fayalite::{
|
||||
memory::{ReadStruct, ReadWriteStruct, WriteStruct},
|
||||
module::{instance_with_loc, reg_builder_with_loc},
|
||||
module::{instance_with_loc, memory_with_init_and_loc, reg_builder_with_loc},
|
||||
prelude::*,
|
||||
reset::ResetType,
|
||||
sim::vcd::VcdWriterDecls,
|
||||
|
|
@ -86,7 +86,7 @@ pub fn mod1() {
|
|||
#[hdl]
|
||||
let child = instance(mod1_child());
|
||||
#[hdl]
|
||||
let o: mod1_child = m.output(Expr::ty(child));
|
||||
let o: mod1_child = m.output(child.ty());
|
||||
connect(o, child);
|
||||
}
|
||||
|
||||
|
|
@ -506,10 +506,10 @@ fn test_enums() {
|
|||
data_out: _,
|
||||
b_out: _,
|
||||
b2_out: _,
|
||||
} = expected;
|
||||
sim.write(sim.io().en, &en);
|
||||
sim.write(sim.io().which_in, &which_in);
|
||||
sim.write(sim.io().data_in, &data_in);
|
||||
} = &expected;
|
||||
sim.write(sim.io().en, en);
|
||||
sim.write(sim.io().which_in, which_in);
|
||||
sim.write(sim.io().data_in, data_in);
|
||||
let io = #[hdl(sim)]
|
||||
IO::<_> {
|
||||
en,
|
||||
|
|
@ -528,7 +528,7 @@ fn test_enums() {
|
|||
);
|
||||
// make sure matching on SimValue<SomeEnum> works
|
||||
#[hdl(sim)]
|
||||
match io.b_out {
|
||||
match &io.b_out {
|
||||
HdlNone => println!("io.b_out is HdlNone"),
|
||||
HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1),
|
||||
}
|
||||
|
|
@ -706,13 +706,13 @@ fn test_memories() {
|
|||
w_en,
|
||||
w_data,
|
||||
w_mask,
|
||||
} = expected;
|
||||
sim.write(sim.io().r.addr, &r_addr);
|
||||
sim.write(sim.io().r.en, &r_en);
|
||||
sim.write(sim.io().w.addr, &w_addr);
|
||||
sim.write(sim.io().w.en, &w_en);
|
||||
sim.write(sim.io().w.data, &w_data);
|
||||
sim.write(sim.io().w.mask, &w_mask);
|
||||
} = &expected;
|
||||
sim.write(sim.io().r.addr, r_addr);
|
||||
sim.write(sim.io().r.en, r_en);
|
||||
sim.write(sim.io().w.addr, w_addr);
|
||||
sim.write(sim.io().w.en, w_en);
|
||||
sim.write(sim.io().w.data, w_data);
|
||||
sim.write(sim.io().w.mask, w_mask);
|
||||
let io = #[hdl(sim)]
|
||||
IO {
|
||||
r_addr,
|
||||
|
|
@ -979,10 +979,10 @@ fn test_memories2() {
|
|||
},
|
||||
) in io_cycles.into_iter().enumerate()
|
||||
{
|
||||
sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static());
|
||||
sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static::<UInt<3>>());
|
||||
sim.write_bool(sim.io().rw.en, en);
|
||||
sim.write_bool(sim.io().rw.wmode, wmode);
|
||||
sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static());
|
||||
sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static::<UInt<2>>());
|
||||
sim.write_bool(sim.io().rw.wmask, wmask);
|
||||
sim.advance_time(SimDuration::from_nanos(250));
|
||||
sim.write_clock(sim.io().rw.clk, true);
|
||||
|
|
@ -1195,9 +1195,9 @@ fn test_memories3() {
|
|||
w_data: [0; 8],
|
||||
w_mask: [false; 8],
|
||||
});
|
||||
sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static());
|
||||
sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static::<UInt<3>>());
|
||||
sim.write_bool(sim.io().r.en, r_en);
|
||||
sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static());
|
||||
sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static::<UInt<3>>());
|
||||
sim.write_bool(sim.io().w.en, w_en);
|
||||
for (i, v) in w_data.into_iter().enumerate() {
|
||||
sim.write_bool_or_int(sim.io().w.data[i], v);
|
||||
|
|
@ -1261,6 +1261,310 @@ fn test_memories3() {
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn many_memories() {
|
||||
#[hdl]
|
||||
let r: Array<ReadStruct<Bool, ConstUsize<4>>, 8> = m.input();
|
||||
#[hdl]
|
||||
let w: Array<WriteStruct<Bool, ConstUsize<4>>, 8> = m.input();
|
||||
for (mem_index, (r, w)) in r.into_iter().zip(w).enumerate() {
|
||||
let mut mem = memory_with_init_and_loc(
|
||||
&format!("mem_{mem_index}"),
|
||||
(0..16)
|
||||
.map(|bit_index| mem_index.pow(5).to_expr()[bit_index])
|
||||
.collect::<Vec<_>>(),
|
||||
SourceLocation::caller(),
|
||||
);
|
||||
connect_any(mem.new_read_port(), r);
|
||||
connect_any(mem.new_write_port(), w);
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
#[test]
|
||||
fn test_many_memories() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(many_memories());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
for r in sim.io().r {
|
||||
sim.write_clock(r.clk, false);
|
||||
}
|
||||
for w in sim.io().w {
|
||||
sim.write_clock(w.clk, false);
|
||||
}
|
||||
#[hdl(cmp_eq)]
|
||||
struct IO {
|
||||
r_addr: UInt<4>,
|
||||
r_en: Bool,
|
||||
r_data: Array<Bool, 8>,
|
||||
w_addr: UInt<4>,
|
||||
w_en: Bool,
|
||||
w_data: Array<Bool, 8>,
|
||||
w_mask: Array<Bool, 8>,
|
||||
}
|
||||
let io_cycles = [
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0_hdl_u4,
|
||||
r_en: false,
|
||||
r_data: [false; 8],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [false; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, true, false, true, false, true, false, true],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: true,
|
||||
w_data: [true; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [true; 8],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: true,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false; 8],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 1_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, true, false, false, false, true],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 2_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, true, false, true],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 3_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, false, false, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 4_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, true, false, true, false, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 5_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, true, true, false, true, true, true],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 6_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, true, false, false, true, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 7_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, true, false, false, false, true],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 8_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, false, false, true],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 9_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, false, true, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0xA_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, true, true, true, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0xB_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, true, true, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0xC_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, false, true, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0xD_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, false, false, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0xE_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, false, false, true],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
#[hdl(sim)]
|
||||
IO {
|
||||
r_addr: 0xF_hdl_u4,
|
||||
r_en: true,
|
||||
r_data: [false, false, false, false, false, false, false, false],
|
||||
w_addr: 0_hdl_u4,
|
||||
w_en: false,
|
||||
w_data: [false; 8],
|
||||
w_mask: [true; 8],
|
||||
},
|
||||
];
|
||||
for (cycle, expected) in io_cycles.into_iter().enumerate() {
|
||||
#[hdl(sim)]
|
||||
let IO {
|
||||
r_addr,
|
||||
r_en,
|
||||
r_data: _,
|
||||
w_addr,
|
||||
w_en,
|
||||
w_data,
|
||||
w_mask,
|
||||
} = &expected;
|
||||
for (((r, w), w_data), w_mask) in sim
|
||||
.io()
|
||||
.r
|
||||
.into_iter()
|
||||
.zip(sim.io().w)
|
||||
.zip(w_data.iter())
|
||||
.zip(w_mask.iter())
|
||||
{
|
||||
sim.write(r.addr, r_addr);
|
||||
sim.write(r.en, r_en);
|
||||
sim.write(w.addr, w_addr);
|
||||
sim.write(w.en, w_en);
|
||||
sim.write(w.data, w_data);
|
||||
sim.write(w.mask, w_mask);
|
||||
}
|
||||
let io = #[hdl(sim)]
|
||||
IO {
|
||||
r_addr,
|
||||
r_en,
|
||||
r_data: std::array::from_fn(|i| sim.read(sim.io().r[i].data)),
|
||||
w_addr,
|
||||
w_en,
|
||||
w_data,
|
||||
w_mask,
|
||||
};
|
||||
assert_eq!(
|
||||
expected,
|
||||
io,
|
||||
"vcd:\n{}\ncycle: {cycle}",
|
||||
String::from_utf8(writer.take()).unwrap(),
|
||||
);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
for r in sim.io().r {
|
||||
sim.write_clock(r.clk, true);
|
||||
}
|
||||
for w in sim.io().w {
|
||||
sim.write_clock(w.clk, true);
|
||||
}
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
for r in sim.io().r {
|
||||
sim.write_clock(r.clk, false);
|
||||
}
|
||||
for w in sim.io().w {
|
||||
sim.write_clock(w.clk, false);
|
||||
}
|
||||
}
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/many_memories.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/many_memories.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn duplicate_names() {
|
||||
#[hdl]
|
||||
|
|
@ -1722,3 +2026,472 @@ fn test_sim_only_connects() {
|
|||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated, extern)]
|
||||
pub fn sim_fork_join<const N: usize>()
|
||||
where
|
||||
ConstUsize<N>: KnownSize,
|
||||
{
|
||||
#[hdl]
|
||||
let clocks: Array<Clock, N> = m.input();
|
||||
#[hdl]
|
||||
let outputs: Array<UInt<8>, N> = m.output();
|
||||
m.extern_module_simulation_fn((clocks, outputs), |(clocks, outputs), mut sim| async move {
|
||||
sim.write(outputs, [0u8; N]).await;
|
||||
loop {
|
||||
sim.fork_join(
|
||||
clocks
|
||||
.into_iter()
|
||||
.zip(outputs)
|
||||
.map(|(clock, output)| {
|
||||
move |mut sim: ExternModuleSimulationState| async move {
|
||||
sim.wait_for_clock_edge(clock).await;
|
||||
let v = sim
|
||||
.read_bool_or_int(output)
|
||||
.await
|
||||
.to_bigint()
|
||||
.try_into()
|
||||
.expect("known to be in range");
|
||||
sim.write(output, 1u8.wrapping_add(v)).await;
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sim_fork_join() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
const N: usize = 3;
|
||||
let mut sim = Simulation::new(sim_fork_join::<N>());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
sim.write(sim.io().clocks, [false; N]);
|
||||
let mut clocks_triggered = [false; N];
|
||||
let mut expected = [0u8; N];
|
||||
for i0 in 0..N {
|
||||
for i1 in 0..N {
|
||||
for i2 in 0..N {
|
||||
for i3 in 0..N {
|
||||
let indexes = [i0, i1, i2, i3];
|
||||
for i in indexes {
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write(sim.io().clocks[i], true);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write(sim.io().clocks[i], false);
|
||||
if !clocks_triggered[i] {
|
||||
expected[i] = expected[i].wrapping_add(1);
|
||||
}
|
||||
clocks_triggered[i] = true;
|
||||
if clocks_triggered == [true; N] {
|
||||
clocks_triggered = [false; N];
|
||||
}
|
||||
let output = sim.read(sim.io().outputs);
|
||||
assert_eq!(output, expected.to_sim_value(), "indexes={indexes:?} i={i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/sim_fork_join.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/sim_fork_join.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated, extern)]
|
||||
pub fn sim_fork_join_scope<const N: usize>()
|
||||
where
|
||||
ConstUsize<N>: KnownSize,
|
||||
{
|
||||
#[hdl]
|
||||
let clocks: Array<Clock, N> = m.input();
|
||||
#[hdl]
|
||||
let outputs: Array<UInt<8>, N> = m.output();
|
||||
m.extern_module_simulation_fn((clocks, outputs), |(clocks, outputs), mut sim| async move {
|
||||
sim.write(outputs, [0u8; N]).await;
|
||||
loop {
|
||||
let written = vec![std::cell::Cell::new(false); N]; // test shared scope
|
||||
let written = &written; // work around move in async move
|
||||
sim.fork_join_scope(|scope, _| async move {
|
||||
let mut spawned = vec![];
|
||||
for i in 0..N {
|
||||
let join_handle =
|
||||
scope.spawn(move |_, mut sim: ExternModuleSimulationState| async move {
|
||||
sim.wait_for_clock_edge(clocks[i]).await;
|
||||
let v = sim
|
||||
.read_bool_or_int(outputs[i])
|
||||
.await
|
||||
.to_bigint()
|
||||
.try_into()
|
||||
.expect("known to be in range");
|
||||
sim.write(outputs[i], 1u8.wrapping_add(v)).await;
|
||||
written[i].set(true);
|
||||
i
|
||||
});
|
||||
if i % 2 == 0 && i < N - 1 {
|
||||
spawned.push((i, join_handle));
|
||||
}
|
||||
}
|
||||
for (i, join_handle) in spawned {
|
||||
assert_eq!(i, join_handle.join().await);
|
||||
}
|
||||
})
|
||||
.await;
|
||||
for written in written {
|
||||
assert!(written.get());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sim_fork_join_scope() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
const N: usize = 3;
|
||||
let mut sim = Simulation::new(sim_fork_join_scope::<N>());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
sim.write(sim.io().clocks, [false; N]);
|
||||
let mut clocks_triggered = [false; N];
|
||||
let mut expected = [0u8; N];
|
||||
for i0 in 0..N {
|
||||
for i1 in 0..N {
|
||||
for i2 in 0..N {
|
||||
for i3 in 0..N {
|
||||
let indexes = [i0, i1, i2, i3];
|
||||
for i in indexes {
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write(sim.io().clocks[i], true);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write(sim.io().clocks[i], false);
|
||||
if !clocks_triggered[i] {
|
||||
expected[i] = expected[i].wrapping_add(1);
|
||||
}
|
||||
clocks_triggered[i] = true;
|
||||
if clocks_triggered == [true; N] {
|
||||
clocks_triggered = [false; N];
|
||||
}
|
||||
let output = sim.read(sim.io().outputs);
|
||||
assert_eq!(output, expected.to_sim_value(), "indexes={indexes:?} i={i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/sim_fork_join_scope.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/sim_fork_join_scope.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated, extern)]
|
||||
pub fn sim_resettable_counter<R: ResetType>() {
|
||||
#[hdl]
|
||||
let cd: ClockDomain<R> = m.input();
|
||||
#[hdl]
|
||||
let out: UInt<8> = m.output();
|
||||
m.extern_module_simulation_fn((cd, out), |(cd, out), mut sim| async move {
|
||||
sim.resettable(
|
||||
cd,
|
||||
|mut sim: ExternModuleSimulationState| async move {
|
||||
sim.write(out, 0u8).await;
|
||||
},
|
||||
|mut sim: ExternModuleSimulationState, ()| async move {
|
||||
loop {
|
||||
sim.wait_for_clock_edge(cd.clk).await;
|
||||
let v: u8 = sim
|
||||
.read(out)
|
||||
.await
|
||||
.to_bigint()
|
||||
.try_into()
|
||||
.expect("known to be in range");
|
||||
sim.write(out, v.wrapping_add(1)).await;
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
});
|
||||
}
|
||||
|
||||
fn test_sim_resettable_counter_helper<R: ResetType>(
|
||||
sim: &mut Simulation<sim_resettable_counter<R>>,
|
||||
immediate_reset: bool,
|
||||
) {
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
sim.write_reset(sim.io().cd.rst, immediate_reset);
|
||||
for _ in 0..2 {
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write_clock(sim.io().cd.clk, true);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
sim.write_reset(sim.io().cd.rst, true);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write_clock(sim.io().cd.clk, true);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
sim.write_reset(sim.io().cd.rst, false);
|
||||
for expected in 0..3u8 {
|
||||
assert_eq!(sim.read(sim.io().out), expected.to_sim_value());
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write_clock(sim.io().cd.clk, true);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sim_resettable_counter_sync() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(sim_resettable_counter::<SyncReset>());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
test_sim_resettable_counter_helper(&mut sim, false);
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/sim_resettable_counter_sync.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/sim_resettable_counter_sync.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sim_resettable_counter_sync_immediate_reset() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(sim_resettable_counter::<SyncReset>());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
test_sim_resettable_counter_helper(&mut sim, true);
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/sim_resettable_counter_sync_immediate_reset.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/sim_resettable_counter_sync_immediate_reset.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sim_resettable_counter_async() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(sim_resettable_counter::<AsyncReset>());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
test_sim_resettable_counter_helper(&mut sim, false);
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/sim_resettable_counter_async.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/sim_resettable_counter_async.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sim_resettable_counter_async_immediate_reset() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(sim_resettable_counter::<AsyncReset>());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
test_sim_resettable_counter_helper(&mut sim, true);
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/sim_resettable_counter_async_immediate_reset.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/sim_resettable_counter_async_immediate_reset.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn phantom_const() {
|
||||
#[hdl]
|
||||
let out: Array<PhantomConst<Vec<String>>, 2> =
|
||||
m.output(Array::new_static(PhantomConst::new_sized(vec![
|
||||
"a".into(),
|
||||
"b".into(),
|
||||
])));
|
||||
let _ = out;
|
||||
#[hdl]
|
||||
let mut mem = memory(PhantomConst::new("mem_element"));
|
||||
mem.depth(1);
|
||||
let port = mem.new_read_port();
|
||||
connect_any(port.addr, 0u8);
|
||||
connect(port.clk, false.to_clock());
|
||||
connect(port.en, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_phantom_const() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(phantom_const());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/phantom_const.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/phantom_const.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated, extern)]
|
||||
pub fn sim_read_past<const N: usize>()
|
||||
where
|
||||
ConstUsize<N>: KnownSize,
|
||||
{
|
||||
#[hdl]
|
||||
let clocks: Array<Clock, N> = m.input();
|
||||
#[hdl]
|
||||
let outputs: Array<UInt<8>, N> = m.output();
|
||||
#[hdl]
|
||||
let past_clocks: Array<Clock, N> = m.output();
|
||||
#[hdl]
|
||||
let past_outputs: Array<UInt<8>, N> = m.output();
|
||||
for clock in clocks {
|
||||
m.register_clock_for_past(clock);
|
||||
}
|
||||
m.extern_module_simulation_fn(
|
||||
(clocks, outputs, past_clocks, past_outputs),
|
||||
|(clocks, outputs, past_clocks, past_outputs), mut sim| async move {
|
||||
sim.write(outputs, [0u8; N]).await;
|
||||
sim.write(past_clocks, [false; N]).await;
|
||||
sim.write(past_outputs, [0u8; N]).await;
|
||||
loop {
|
||||
sim.fork_join_scope(|scope, _| async move {
|
||||
for (clock, output) in clocks.into_iter().zip(outputs) {
|
||||
scope.spawn_detached(
|
||||
move |_, mut sim: ExternModuleSimulationState| async move {
|
||||
sim.wait_for_clock_edge(clock).await;
|
||||
dbg!(clock);
|
||||
let v = sim
|
||||
.read_bool_or_int(output)
|
||||
.await
|
||||
.to_bigint()
|
||||
.try_into()
|
||||
.expect("known to be in range");
|
||||
sim.write(output, 1u8.wrapping_add(v)).await;
|
||||
let past_outputs_v = sim.read_past(outputs, clock).await;
|
||||
dbg!(&past_outputs_v);
|
||||
sim.write(past_outputs, past_outputs_v).await;
|
||||
let past_clocks_v = sim.read_past(clocks, clock).await;
|
||||
dbg!(&past_clocks_v);
|
||||
sim.write(past_clocks, past_clocks_v).await;
|
||||
},
|
||||
);
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sim_read_past() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
const N: usize = 3;
|
||||
let mut sim = Simulation::new(sim_read_past::<N>());
|
||||
// sim.set_breakpoints_unstable(Default::default(), true);
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
sim.write(sim.io().clocks, [false; N]);
|
||||
let mut clocks_triggered = [false; N];
|
||||
let mut expected = [0u8; N];
|
||||
let mut past_clocks_expected = [false; N];
|
||||
let mut past_expected = expected;
|
||||
for i0 in 0..N {
|
||||
for i1 in 0..N {
|
||||
for i2 in 0..N {
|
||||
for i3 in 0..N {
|
||||
let indexes = [i0, i1, i2, i3];
|
||||
for i in indexes {
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write(sim.io().clocks[i], true);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write(sim.io().clocks[i], false);
|
||||
if !clocks_triggered[i] {
|
||||
past_expected = expected;
|
||||
expected[i] = expected[i].wrapping_add(1);
|
||||
past_clocks_expected = [false; N];
|
||||
past_clocks_expected[i] = true;
|
||||
}
|
||||
dbg!(past_expected);
|
||||
clocks_triggered[i] = true;
|
||||
if clocks_triggered == [true; N] {
|
||||
clocks_triggered = [false; N];
|
||||
}
|
||||
let output = sim.read(sim.io().outputs);
|
||||
assert_eq!(output, expected.to_sim_value(), "indexes={indexes:?} i={i}");
|
||||
let past_clocks = sim.read(sim.io().past_clocks);
|
||||
assert_eq!(
|
||||
past_clocks,
|
||||
past_clocks_expected
|
||||
.to_sim_value_with_type(Array::<Clock, N>::default()),
|
||||
"indexes={indexes:?} i={i}"
|
||||
);
|
||||
let past_outputs = sim.read(sim.io().past_outputs);
|
||||
dbg!(&past_outputs);
|
||||
assert_eq!(
|
||||
past_outputs,
|
||||
past_expected.to_sim_value(),
|
||||
"indexes={indexes:?} i={i}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/sim_read_past.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/sim_read_past.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -826,9 +826,9 @@ Simulation {
|
|||
}.write_index,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "array_rw",
|
||||
children: [
|
||||
|
|
@ -1699,7 +1699,12 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 34 μs,
|
||||
clocks_triggered: [],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 34 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -122,9 +122,9 @@ Simulation {
|
|||
}.i,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "conditional_assignment_last",
|
||||
children: [
|
||||
|
|
@ -177,7 +177,12 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 2 μs,
|
||||
clocks_triggered: [],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 2 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -98,9 +98,9 @@ Simulation {
|
|||
}.o,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "connect_const",
|
||||
children: [
|
||||
|
|
@ -130,7 +130,12 @@ Simulation {
|
|||
],
|
||||
trace_memories: {},
|
||||
trace_writers: [],
|
||||
instant: 0 s,
|
||||
clocks_triggered: [],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 0 s,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ Simulation {
|
|||
},
|
||||
SlotDebugData {
|
||||
name: "",
|
||||
ty: Bool,
|
||||
ty: UInt<1>,
|
||||
},
|
||||
SlotDebugData {
|
||||
name: "",
|
||||
|
|
@ -51,12 +51,12 @@ Simulation {
|
|||
insns: [
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
0: Const {
|
||||
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
|
||||
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
|
||||
value: 0x1,
|
||||
},
|
||||
1: Copy {
|
||||
dest: StatePartIndex<BigSlots>(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset },
|
||||
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:4:1
|
||||
2: Copy {
|
||||
|
|
@ -141,9 +141,9 @@ Simulation {
|
|||
}.reset_out,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "connect_const_reset",
|
||||
children: [
|
||||
|
|
@ -197,7 +197,12 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 1 μs,
|
||||
clocks_triggered: [],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 1 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -100,51 +100,51 @@ Simulation {
|
|||
dest: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.rst", ty: AsyncReset },
|
||||
},
|
||||
3: IsNonZeroDestIsSmall {
|
||||
dest: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.clk", ty: Clock },
|
||||
},
|
||||
4: AndSmall {
|
||||
dest: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
rhs: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
3: Const {
|
||||
5: Const {
|
||||
dest: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
|
||||
value: 0x3,
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:3:1
|
||||
4: BranchIfZero {
|
||||
target: 6,
|
||||
6: BranchIfZero {
|
||||
target: 8,
|
||||
value: StatePartIndex<BigSlots>(6), // (0x0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
5: Copy {
|
||||
7: Copy {
|
||||
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
|
||||
src: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
6: Add {
|
||||
8: Add {
|
||||
dest: StatePartIndex<BigSlots>(8), // (0x4) SlotDebugData { name: "", ty: UInt<5> },
|
||||
lhs: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
|
||||
rhs: StatePartIndex<BigSlots>(7), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
|
||||
},
|
||||
7: CastToUInt {
|
||||
9: CastToUInt {
|
||||
dest: StatePartIndex<BigSlots>(9), // (0x4) SlotDebugData { name: "", ty: UInt<4> },
|
||||
src: StatePartIndex<BigSlots>(8), // (0x4) SlotDebugData { name: "", ty: UInt<5> },
|
||||
dest_width: 4,
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:4:1
|
||||
8: Copy {
|
||||
10: Copy {
|
||||
dest: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
|
||||
src: StatePartIndex<BigSlots>(9), // (0x4) SlotDebugData { name: "", ty: UInt<4> },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:6:1
|
||||
9: Copy {
|
||||
11: Copy {
|
||||
dest: StatePartIndex<BigSlots>(2), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count", ty: UInt<4> },
|
||||
src: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:3:1
|
||||
10: IsNonZeroDestIsSmall {
|
||||
dest: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.clk", ty: Clock },
|
||||
},
|
||||
11: AndSmall {
|
||||
dest: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
rhs: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
12: BranchIfSmallNonZero {
|
||||
target: 16,
|
||||
value: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
|
|
@ -261,9 +261,9 @@ Simulation {
|
|||
}.count,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "counter",
|
||||
children: [
|
||||
|
|
@ -329,7 +329,7 @@ Simulation {
|
|||
index: StatePartIndex<BigSlots>(0),
|
||||
},
|
||||
state: 0x1,
|
||||
last_state: 0x1,
|
||||
last_state: 0x0,
|
||||
},
|
||||
SimTrace {
|
||||
id: TraceScalarId(1),
|
||||
|
|
@ -355,7 +355,7 @@ Simulation {
|
|||
ty: UInt<4>,
|
||||
},
|
||||
state: 0x3,
|
||||
last_state: 0x3,
|
||||
last_state: 0x2,
|
||||
},
|
||||
],
|
||||
trace_memories: {},
|
||||
|
|
@ -368,9 +368,14 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 66 μs,
|
||||
clocks_triggered: [
|
||||
StatePartIndex<SmallSlots>(1),
|
||||
],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 66 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -26,192 +26,192 @@ b11 $
|
|||
0!
|
||||
#3000000
|
||||
1!
|
||||
b100 $
|
||||
b100 #
|
||||
b100 $
|
||||
#4000000
|
||||
0!
|
||||
#5000000
|
||||
1!
|
||||
b101 $
|
||||
b101 #
|
||||
b101 $
|
||||
#6000000
|
||||
0!
|
||||
#7000000
|
||||
1!
|
||||
b110 $
|
||||
b110 #
|
||||
b110 $
|
||||
#8000000
|
||||
0!
|
||||
#9000000
|
||||
1!
|
||||
b111 $
|
||||
b111 #
|
||||
b111 $
|
||||
#10000000
|
||||
0!
|
||||
#11000000
|
||||
1!
|
||||
b1000 $
|
||||
b1000 #
|
||||
b1000 $
|
||||
#12000000
|
||||
0!
|
||||
#13000000
|
||||
1!
|
||||
b1001 $
|
||||
b1001 #
|
||||
b1001 $
|
||||
#14000000
|
||||
0!
|
||||
#15000000
|
||||
1!
|
||||
b1010 $
|
||||
b1010 #
|
||||
b1010 $
|
||||
#16000000
|
||||
0!
|
||||
#17000000
|
||||
1!
|
||||
b1011 $
|
||||
b1011 #
|
||||
b1011 $
|
||||
#18000000
|
||||
0!
|
||||
#19000000
|
||||
1!
|
||||
b1100 $
|
||||
b1100 #
|
||||
b1100 $
|
||||
#20000000
|
||||
0!
|
||||
#21000000
|
||||
1!
|
||||
b1101 $
|
||||
b1101 #
|
||||
b1101 $
|
||||
#22000000
|
||||
0!
|
||||
#23000000
|
||||
1!
|
||||
b1110 $
|
||||
b1110 #
|
||||
b1110 $
|
||||
#24000000
|
||||
0!
|
||||
#25000000
|
||||
1!
|
||||
b1111 $
|
||||
b1111 #
|
||||
b1111 $
|
||||
#26000000
|
||||
0!
|
||||
#27000000
|
||||
1!
|
||||
b0 $
|
||||
b0 #
|
||||
b0 $
|
||||
#28000000
|
||||
0!
|
||||
#29000000
|
||||
1!
|
||||
b1 $
|
||||
b1 #
|
||||
b1 $
|
||||
#30000000
|
||||
0!
|
||||
#31000000
|
||||
1!
|
||||
b10 $
|
||||
b10 #
|
||||
b10 $
|
||||
#32000000
|
||||
0!
|
||||
#33000000
|
||||
1!
|
||||
b11 $
|
||||
b11 #
|
||||
b11 $
|
||||
#34000000
|
||||
0!
|
||||
#35000000
|
||||
1!
|
||||
b100 $
|
||||
b100 #
|
||||
b100 $
|
||||
#36000000
|
||||
0!
|
||||
#37000000
|
||||
1!
|
||||
b101 $
|
||||
b101 #
|
||||
b101 $
|
||||
#38000000
|
||||
0!
|
||||
#39000000
|
||||
1!
|
||||
b110 $
|
||||
b110 #
|
||||
b110 $
|
||||
#40000000
|
||||
0!
|
||||
#41000000
|
||||
1!
|
||||
b111 $
|
||||
b111 #
|
||||
b111 $
|
||||
#42000000
|
||||
0!
|
||||
#43000000
|
||||
1!
|
||||
b1000 $
|
||||
b1000 #
|
||||
b1000 $
|
||||
#44000000
|
||||
0!
|
||||
#45000000
|
||||
1!
|
||||
b1001 $
|
||||
b1001 #
|
||||
b1001 $
|
||||
#46000000
|
||||
0!
|
||||
#47000000
|
||||
1!
|
||||
b1010 $
|
||||
b1010 #
|
||||
b1010 $
|
||||
#48000000
|
||||
0!
|
||||
#49000000
|
||||
1!
|
||||
b1011 $
|
||||
b1011 #
|
||||
b1011 $
|
||||
#50000000
|
||||
0!
|
||||
#51000000
|
||||
1!
|
||||
b1100 $
|
||||
b1100 #
|
||||
b1100 $
|
||||
#52000000
|
||||
0!
|
||||
#53000000
|
||||
1!
|
||||
b1101 $
|
||||
b1101 #
|
||||
b1101 $
|
||||
#54000000
|
||||
0!
|
||||
#55000000
|
||||
1!
|
||||
b1110 $
|
||||
b1110 #
|
||||
b1110 $
|
||||
#56000000
|
||||
0!
|
||||
#57000000
|
||||
1!
|
||||
b1111 $
|
||||
b1111 #
|
||||
b1111 $
|
||||
#58000000
|
||||
0!
|
||||
#59000000
|
||||
1!
|
||||
b0 $
|
||||
b0 #
|
||||
b0 $
|
||||
#60000000
|
||||
0!
|
||||
#61000000
|
||||
1!
|
||||
b1 $
|
||||
b1 #
|
||||
b1 $
|
||||
#62000000
|
||||
0!
|
||||
#63000000
|
||||
1!
|
||||
b10 $
|
||||
b10 #
|
||||
b10 $
|
||||
#64000000
|
||||
0!
|
||||
#65000000
|
||||
1!
|
||||
b11 $
|
||||
b11 #
|
||||
b11 $
|
||||
#66000000
|
||||
|
|
|
|||
|
|
@ -112,21 +112,21 @@ Simulation {
|
|||
dest: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.rst", ty: SyncReset },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
6: Const {
|
||||
dest: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
|
||||
value: 0x3,
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:3:1
|
||||
7: IsNonZeroDestIsSmall {
|
||||
6: IsNonZeroDestIsSmall {
|
||||
dest: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.clk", ty: Clock },
|
||||
},
|
||||
8: AndSmall {
|
||||
7: AndSmall {
|
||||
dest: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
rhs: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
8: Const {
|
||||
dest: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
|
||||
value: 0x3,
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:3:1
|
||||
9: BranchIfSmallZero {
|
||||
target: 14,
|
||||
value: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
|
|
@ -242,9 +242,9 @@ Simulation {
|
|||
}.count,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "counter",
|
||||
children: [
|
||||
|
|
@ -310,7 +310,7 @@ Simulation {
|
|||
index: StatePartIndex<BigSlots>(0),
|
||||
},
|
||||
state: 0x1,
|
||||
last_state: 0x1,
|
||||
last_state: 0x0,
|
||||
},
|
||||
SimTrace {
|
||||
id: TraceScalarId(1),
|
||||
|
|
@ -336,7 +336,7 @@ Simulation {
|
|||
ty: UInt<4>,
|
||||
},
|
||||
state: 0x3,
|
||||
last_state: 0x3,
|
||||
last_state: 0x2,
|
||||
},
|
||||
],
|
||||
trace_memories: {},
|
||||
|
|
@ -349,9 +349,14 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 66 μs,
|
||||
clocks_triggered: [
|
||||
StatePartIndex<SmallSlots>(1),
|
||||
],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 66 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -16,199 +16,199 @@ b0 $
|
|||
$end
|
||||
#1000000
|
||||
1!
|
||||
b11 $
|
||||
b11 #
|
||||
b11 $
|
||||
0"
|
||||
#2000000
|
||||
0!
|
||||
#3000000
|
||||
1!
|
||||
b100 $
|
||||
b100 #
|
||||
b100 $
|
||||
#4000000
|
||||
0!
|
||||
#5000000
|
||||
1!
|
||||
b101 $
|
||||
b101 #
|
||||
b101 $
|
||||
#6000000
|
||||
0!
|
||||
#7000000
|
||||
1!
|
||||
b110 $
|
||||
b110 #
|
||||
b110 $
|
||||
#8000000
|
||||
0!
|
||||
#9000000
|
||||
1!
|
||||
b111 $
|
||||
b111 #
|
||||
b111 $
|
||||
#10000000
|
||||
0!
|
||||
#11000000
|
||||
1!
|
||||
b1000 $
|
||||
b1000 #
|
||||
b1000 $
|
||||
#12000000
|
||||
0!
|
||||
#13000000
|
||||
1!
|
||||
b1001 $
|
||||
b1001 #
|
||||
b1001 $
|
||||
#14000000
|
||||
0!
|
||||
#15000000
|
||||
1!
|
||||
b1010 $
|
||||
b1010 #
|
||||
b1010 $
|
||||
#16000000
|
||||
0!
|
||||
#17000000
|
||||
1!
|
||||
b1011 $
|
||||
b1011 #
|
||||
b1011 $
|
||||
#18000000
|
||||
0!
|
||||
#19000000
|
||||
1!
|
||||
b1100 $
|
||||
b1100 #
|
||||
b1100 $
|
||||
#20000000
|
||||
0!
|
||||
#21000000
|
||||
1!
|
||||
b1101 $
|
||||
b1101 #
|
||||
b1101 $
|
||||
#22000000
|
||||
0!
|
||||
#23000000
|
||||
1!
|
||||
b1110 $
|
||||
b1110 #
|
||||
b1110 $
|
||||
#24000000
|
||||
0!
|
||||
#25000000
|
||||
1!
|
||||
b1111 $
|
||||
b1111 #
|
||||
b1111 $
|
||||
#26000000
|
||||
0!
|
||||
#27000000
|
||||
1!
|
||||
b0 $
|
||||
b0 #
|
||||
b0 $
|
||||
#28000000
|
||||
0!
|
||||
#29000000
|
||||
1!
|
||||
b1 $
|
||||
b1 #
|
||||
b1 $
|
||||
#30000000
|
||||
0!
|
||||
#31000000
|
||||
1!
|
||||
b10 $
|
||||
b10 #
|
||||
b10 $
|
||||
#32000000
|
||||
0!
|
||||
#33000000
|
||||
1!
|
||||
b11 $
|
||||
b11 #
|
||||
b11 $
|
||||
#34000000
|
||||
0!
|
||||
#35000000
|
||||
1!
|
||||
b100 $
|
||||
b100 #
|
||||
b100 $
|
||||
#36000000
|
||||
0!
|
||||
#37000000
|
||||
1!
|
||||
b101 $
|
||||
b101 #
|
||||
b101 $
|
||||
#38000000
|
||||
0!
|
||||
#39000000
|
||||
1!
|
||||
b110 $
|
||||
b110 #
|
||||
b110 $
|
||||
#40000000
|
||||
0!
|
||||
#41000000
|
||||
1!
|
||||
b111 $
|
||||
b111 #
|
||||
b111 $
|
||||
#42000000
|
||||
0!
|
||||
#43000000
|
||||
1!
|
||||
b1000 $
|
||||
b1000 #
|
||||
b1000 $
|
||||
#44000000
|
||||
0!
|
||||
#45000000
|
||||
1!
|
||||
b1001 $
|
||||
b1001 #
|
||||
b1001 $
|
||||
#46000000
|
||||
0!
|
||||
#47000000
|
||||
1!
|
||||
b1010 $
|
||||
b1010 #
|
||||
b1010 $
|
||||
#48000000
|
||||
0!
|
||||
#49000000
|
||||
1!
|
||||
b1011 $
|
||||
b1011 #
|
||||
b1011 $
|
||||
#50000000
|
||||
0!
|
||||
#51000000
|
||||
1!
|
||||
b1100 $
|
||||
b1100 #
|
||||
b1100 $
|
||||
#52000000
|
||||
0!
|
||||
#53000000
|
||||
1!
|
||||
b1101 $
|
||||
b1101 #
|
||||
b1101 $
|
||||
#54000000
|
||||
0!
|
||||
#55000000
|
||||
1!
|
||||
b1110 $
|
||||
b1110 #
|
||||
b1110 $
|
||||
#56000000
|
||||
0!
|
||||
#57000000
|
||||
1!
|
||||
b1111 $
|
||||
b1111 #
|
||||
b1111 $
|
||||
#58000000
|
||||
0!
|
||||
#59000000
|
||||
1!
|
||||
b0 $
|
||||
b0 #
|
||||
b0 $
|
||||
#60000000
|
||||
0!
|
||||
#61000000
|
||||
1!
|
||||
b1 $
|
||||
b1 #
|
||||
b1 $
|
||||
#62000000
|
||||
0!
|
||||
#63000000
|
||||
1!
|
||||
b10 $
|
||||
b10 #
|
||||
b10 $
|
||||
#64000000
|
||||
0!
|
||||
#65000000
|
||||
1!
|
||||
b11 $
|
||||
b11 #
|
||||
b11 $
|
||||
#66000000
|
||||
|
|
|
|||
|
|
@ -102,9 +102,9 @@ Simulation {
|
|||
uninitialized_ios: {},
|
||||
io_targets: {},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "duplicate_names",
|
||||
children: [
|
||||
|
|
@ -160,7 +160,12 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 1 μs,
|
||||
clocks_triggered: [],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 1 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -1003,65 +1003,64 @@ Simulation {
|
|||
dest: StatePartIndex<SmallSlots>(5), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.rst", ty: SyncReset },
|
||||
},
|
||||
97: IsNonZeroDestIsSmall {
|
||||
dest: StatePartIndex<SmallSlots>(4), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.clk", ty: Clock },
|
||||
},
|
||||
98: AndSmall {
|
||||
dest: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
lhs: StatePartIndex<SmallSlots>(4), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
rhs: StatePartIndex<SmallSlots>(2), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
97: Const {
|
||||
99: Const {
|
||||
dest: StatePartIndex<BigSlots>(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> },
|
||||
value: 0x0,
|
||||
},
|
||||
98: Copy {
|
||||
100: Copy {
|
||||
dest: StatePartIndex<BigSlots>(26), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
|
||||
src: StatePartIndex<BigSlots>(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:12:1
|
||||
99: BranchIfZero {
|
||||
target: 107,
|
||||
101: BranchIfZero {
|
||||
target: 109,
|
||||
value: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::en", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:13:1
|
||||
100: BranchIfZero {
|
||||
target: 102,
|
||||
102: BranchIfZero {
|
||||
target: 104,
|
||||
value: StatePartIndex<BigSlots>(46), // (0x0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:14:1
|
||||
101: Copy {
|
||||
103: Copy {
|
||||
dest: StatePartIndex<BigSlots>(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
|
||||
src: StatePartIndex<BigSlots>(26), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:13:1
|
||||
102: BranchIfNonZero {
|
||||
target: 107,
|
||||
104: BranchIfNonZero {
|
||||
target: 109,
|
||||
value: StatePartIndex<BigSlots>(46), // (0x0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:15:1
|
||||
103: BranchIfZero {
|
||||
target: 105,
|
||||
105: BranchIfZero {
|
||||
target: 107,
|
||||
value: StatePartIndex<BigSlots>(48), // (0x0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:16:1
|
||||
104: Copy {
|
||||
106: Copy {
|
||||
dest: StatePartIndex<BigSlots>(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
|
||||
src: StatePartIndex<BigSlots>(65), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:15:1
|
||||
105: BranchIfNonZero {
|
||||
target: 107,
|
||||
107: BranchIfNonZero {
|
||||
target: 109,
|
||||
value: StatePartIndex<BigSlots>(48), // (0x0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:17:1
|
||||
106: Copy {
|
||||
108: Copy {
|
||||
dest: StatePartIndex<BigSlots>(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
|
||||
src: StatePartIndex<BigSlots>(87), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:11:1
|
||||
107: IsNonZeroDestIsSmall {
|
||||
dest: StatePartIndex<SmallSlots>(4), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.clk", ty: Clock },
|
||||
},
|
||||
108: AndSmall {
|
||||
dest: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
lhs: StatePartIndex<SmallSlots>(4), // (0x1 1) SlotDebugData { name: "", ty: Bool },
|
||||
rhs: StatePartIndex<SmallSlots>(2), // (0x0 0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:10:1
|
||||
109: Copy {
|
||||
dest: StatePartIndex<BigSlots>(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} },
|
||||
|
|
@ -1454,9 +1453,9 @@ Simulation {
|
|||
}.which_out,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "enums",
|
||||
children: [
|
||||
|
|
@ -1744,7 +1743,7 @@ Simulation {
|
|||
index: StatePartIndex<BigSlots>(0),
|
||||
},
|
||||
state: 0x1,
|
||||
last_state: 0x1,
|
||||
last_state: 0x0,
|
||||
},
|
||||
SimTrace {
|
||||
id: TraceScalarId(1),
|
||||
|
|
@ -1924,9 +1923,14 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 16 μs,
|
||||
clocks_triggered: [
|
||||
StatePartIndex<SmallSlots>(3),
|
||||
],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 16 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -102,6 +102,7 @@ Simulation {
|
|||
}.o,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [
|
||||
SimulationExternModuleState {
|
||||
|
|
@ -136,6 +137,7 @@ Simulation {
|
|||
},
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
sim: ExternModuleSimulation {
|
||||
generator: SimGeneratorFn {
|
||||
|
|
@ -186,14 +188,8 @@ Simulation {
|
|||
running_generator: Some(
|
||||
...,
|
||||
),
|
||||
wait_targets: {
|
||||
Instant(
|
||||
20.500000000000 μs,
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "extern_module",
|
||||
children: [
|
||||
|
|
@ -234,7 +230,7 @@ Simulation {
|
|||
index: StatePartIndex<BigSlots>(1),
|
||||
},
|
||||
state: 0x1,
|
||||
last_state: 0x1,
|
||||
last_state: 0x0,
|
||||
},
|
||||
],
|
||||
trace_memories: {},
|
||||
|
|
@ -247,7 +243,21 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 20 μs,
|
||||
clocks_triggered: [],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 20 μs,
|
||||
events: {
|
||||
Event {
|
||||
instant: 20.500000000000 μs,
|
||||
kind: ExternModule(
|
||||
0,
|
||||
),
|
||||
}: Wakers(
|
||||
1,
|
||||
),
|
||||
},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -6,8 +6,9 @@ $upscope $end
|
|||
$enddefinitions $end
|
||||
$dumpvars
|
||||
0!
|
||||
1"
|
||||
0"
|
||||
$end
|
||||
1"
|
||||
#500000
|
||||
#1500000
|
||||
0"
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ Simulation {
|
|||
}.o,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [
|
||||
SimulationExternModuleState {
|
||||
|
|
@ -167,6 +168,7 @@ Simulation {
|
|||
},
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
sim: ExternModuleSimulation {
|
||||
generator: SimGeneratorFn {
|
||||
|
|
@ -234,55 +236,8 @@ Simulation {
|
|||
running_generator: Some(
|
||||
...,
|
||||
),
|
||||
wait_targets: {
|
||||
Change {
|
||||
key: CompiledValue {
|
||||
layout: CompiledTypeLayout {
|
||||
ty: Clock,
|
||||
layout: TypeLayout {
|
||||
small_slots: StatePartLayout<SmallSlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
..
|
||||
},
|
||||
big_slots: StatePartLayout<BigSlots> {
|
||||
len: 1,
|
||||
debug_data: [
|
||||
SlotDebugData {
|
||||
name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk",
|
||||
ty: Clock,
|
||||
},
|
||||
],
|
||||
..
|
||||
},
|
||||
sim_only_slots: StatePartLayout<SimOnlySlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
layout_data: [],
|
||||
..
|
||||
},
|
||||
},
|
||||
body: Scalar,
|
||||
},
|
||||
range: TypeIndexRange {
|
||||
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
|
||||
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
|
||||
sim_only_slots: StatePartIndexRange<SimOnlySlots> { start: 0, len: 0 },
|
||||
},
|
||||
write: None,
|
||||
},
|
||||
value: SimValue {
|
||||
ty: Clock,
|
||||
value: OpaqueSimValue {
|
||||
bits: 0x1_u1,
|
||||
sim_only_values: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "extern_module2",
|
||||
children: [
|
||||
|
|
@ -356,7 +311,113 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 60 μs,
|
||||
clocks_triggered: [],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 60 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {
|
||||
SensitivitySet {
|
||||
id: 59,
|
||||
values: {
|
||||
CompiledValue {
|
||||
layout: CompiledTypeLayout {
|
||||
ty: Clock,
|
||||
layout: TypeLayout {
|
||||
small_slots: StatePartLayout<SmallSlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
..
|
||||
},
|
||||
big_slots: StatePartLayout<BigSlots> {
|
||||
len: 1,
|
||||
debug_data: [
|
||||
SlotDebugData {
|
||||
name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk",
|
||||
ty: Clock,
|
||||
},
|
||||
],
|
||||
..
|
||||
},
|
||||
sim_only_slots: StatePartLayout<SimOnlySlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
layout_data: [],
|
||||
..
|
||||
},
|
||||
},
|
||||
body: Scalar,
|
||||
},
|
||||
range: TypeIndexRange {
|
||||
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
|
||||
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
|
||||
sim_only_slots: StatePartIndexRange<SimOnlySlots> { start: 0, len: 0 },
|
||||
},
|
||||
write: None,
|
||||
}: SimValue {
|
||||
ty: Clock,
|
||||
value: OpaqueSimValue {
|
||||
bits: 0x1_u1,
|
||||
sim_only_values: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
changed: Cell {
|
||||
value: false,
|
||||
},
|
||||
..
|
||||
},
|
||||
},
|
||||
waiting_sensitivity_sets_by_compiled_value: {
|
||||
CompiledValue {
|
||||
layout: CompiledTypeLayout {
|
||||
ty: Clock,
|
||||
layout: TypeLayout {
|
||||
small_slots: StatePartLayout<SmallSlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
..
|
||||
},
|
||||
big_slots: StatePartLayout<BigSlots> {
|
||||
len: 1,
|
||||
debug_data: [
|
||||
SlotDebugData {
|
||||
name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk",
|
||||
ty: Clock,
|
||||
},
|
||||
],
|
||||
..
|
||||
},
|
||||
sim_only_slots: StatePartLayout<SimOnlySlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
layout_data: [],
|
||||
..
|
||||
},
|
||||
},
|
||||
body: Scalar,
|
||||
},
|
||||
range: TypeIndexRange {
|
||||
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
|
||||
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
|
||||
sim_only_slots: StatePartIndexRange<SimOnlySlots> { start: 0, len: 0 },
|
||||
},
|
||||
write: None,
|
||||
}: (
|
||||
SimValue {
|
||||
ty: Clock,
|
||||
value: OpaqueSimValue {
|
||||
bits: 0x1_u1,
|
||||
sim_only_values: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
SensitivitySet {
|
||||
id: 59,
|
||||
..
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
..
|
||||
}
|
||||
|
|
@ -8,8 +8,9 @@ $enddefinitions $end
|
|||
$dumpvars
|
||||
1!
|
||||
0"
|
||||
b1001000 #
|
||||
b0 #
|
||||
$end
|
||||
b1001000 #
|
||||
#1000000
|
||||
1"
|
||||
b1100101 #
|
||||
|
|
|
|||
7787
crates/fayalite/tests/sim/expected/many_memories.txt
Normal file
7787
crates/fayalite/tests/sim/expected/many_memories.txt
Normal file
File diff suppressed because it is too large
Load diff
2596
crates/fayalite/tests/sim/expected/many_memories.vcd
Normal file
2596
crates/fayalite/tests/sim/expected/many_memories.vcd
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -719,9 +719,9 @@ Simulation {
|
|||
}.w.mask.1,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "memories",
|
||||
children: [
|
||||
|
|
@ -1616,10 +1616,15 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 22 μs,
|
||||
clocks_triggered: [
|
||||
StatePartIndex<SmallSlots>(1),
|
||||
StatePartIndex<SmallSlots>(6),
|
||||
],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 22 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -234,13 +234,13 @@ b100000 6
|
|||
b10000 9
|
||||
b100000 I
|
||||
1#
|
||||
1(
|
||||
1/
|
||||
14
|
||||
b10000 $
|
||||
b100000 %
|
||||
1(
|
||||
1/
|
||||
b10000 0
|
||||
b100000 1
|
||||
14
|
||||
#4000000
|
||||
0#
|
||||
0(
|
||||
|
|
@ -256,11 +256,11 @@ b1000000 6
|
|||
b10000 9
|
||||
b1000000 I
|
||||
1#
|
||||
b1000000 %
|
||||
1(
|
||||
1/
|
||||
14
|
||||
b1000000 %
|
||||
b1000000 1
|
||||
14
|
||||
#6000000
|
||||
0#
|
||||
0(
|
||||
|
|
@ -278,11 +278,11 @@ b1100000 6
|
|||
b1010000 9
|
||||
b1000000 I
|
||||
1#
|
||||
b1010000 $
|
||||
1(
|
||||
1/
|
||||
14
|
||||
b1010000 $
|
||||
b1010000 0
|
||||
14
|
||||
#8000000
|
||||
0#
|
||||
0(
|
||||
|
|
|
|||
|
|
@ -677,9 +677,9 @@ Simulation {
|
|||
}.rw.wmode,
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "memories2",
|
||||
children: [
|
||||
|
|
@ -1260,9 +1260,14 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 22 μs,
|
||||
clocks_triggered: [
|
||||
StatePartIndex<SmallSlots>(3),
|
||||
],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 22 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -100,8 +100,8 @@ $end
|
|||
1)
|
||||
#1250000
|
||||
1#
|
||||
1*
|
||||
b11 $
|
||||
1*
|
||||
sHdlSome\x20(1) +
|
||||
1,
|
||||
#1500000
|
||||
|
|
@ -113,8 +113,8 @@ sHdlSome\x20(1) +
|
|||
0)
|
||||
#2250000
|
||||
1#
|
||||
1*
|
||||
b0 $
|
||||
1*
|
||||
sHdlNone\x20(0) +
|
||||
0,
|
||||
#2500000
|
||||
|
|
@ -303,8 +303,8 @@ b11 !
|
|||
b11 (
|
||||
#17250000
|
||||
1#
|
||||
1*
|
||||
b11 $
|
||||
1*
|
||||
sHdlSome\x20(1) +
|
||||
1,
|
||||
#17500000
|
||||
|
|
@ -316,8 +316,8 @@ b10 !
|
|||
b10 (
|
||||
#18250000
|
||||
1#
|
||||
1*
|
||||
b0 $
|
||||
1*
|
||||
sHdlNone\x20(0) +
|
||||
0,
|
||||
#18500000
|
||||
|
|
@ -339,8 +339,8 @@ b1 !
|
|||
b1 (
|
||||
#20250000
|
||||
1#
|
||||
1*
|
||||
b1 $
|
||||
1*
|
||||
sHdlSome\x20(1) +
|
||||
#20500000
|
||||
#20750000
|
||||
|
|
@ -353,8 +353,8 @@ b0 (
|
|||
0)
|
||||
#21250000
|
||||
1#
|
||||
1*
|
||||
b0 $
|
||||
1*
|
||||
sHdlNone\x20(0) +
|
||||
#21500000
|
||||
#21750000
|
||||
|
|
|
|||
|
|
@ -1761,9 +1761,9 @@ Simulation {
|
|||
}.w.mask[7],
|
||||
},
|
||||
did_initial_settle: true,
|
||||
clocks_for_past: {},
|
||||
},
|
||||
extern_modules: [],
|
||||
state_ready_to_run: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "memories3",
|
||||
children: [
|
||||
|
|
@ -3275,10 +3275,15 @@ Simulation {
|
|||
},
|
||||
),
|
||||
],
|
||||
instant: 15 μs,
|
||||
clocks_triggered: [
|
||||
StatePartIndex<SmallSlots>(1),
|
||||
StatePartIndex<SmallSlots>(6),
|
||||
],
|
||||
event_queue: EventQueue(EventQueueData {
|
||||
instant: 15 μs,
|
||||
events: {},
|
||||
}),
|
||||
waiting_sensitivity_sets_by_address: {},
|
||||
waiting_sensitivity_sets_by_compiled_value: {},
|
||||
..
|
||||
}
|
||||
|
|
@ -420,6 +420,10 @@ b10000 T
|
|||
1\
|
||||
#3250000
|
||||
1#
|
||||
b110100 %
|
||||
b1111000 '
|
||||
b10011010 (
|
||||
b11110000 +
|
||||
1.
|
||||
1A
|
||||
b110100 C
|
||||
|
|
@ -427,10 +431,6 @@ b1111000 E
|
|||
b10011010 F
|
||||
b11110000 I
|
||||
1L
|
||||
b110100 %
|
||||
b1111000 '
|
||||
b10011010 (
|
||||
b11110000 +
|
||||
#3500000
|
||||
#3750000
|
||||
0#
|
||||
|
|
@ -508,6 +508,14 @@ b1010100 '"
|
|||
b110010 /"
|
||||
b10000 7"
|
||||
1#
|
||||
b11111110 $
|
||||
b11011100 %
|
||||
b10111010 &
|
||||
b10011000 '
|
||||
b1110110 (
|
||||
b1010100 )
|
||||
b110010 *
|
||||
b10000 +
|
||||
1.
|
||||
1A
|
||||
b11111110 B
|
||||
|
|
@ -519,14 +527,6 @@ b1010100 G
|
|||
b110010 H
|
||||
b10000 I
|
||||
1L
|
||||
b11111110 $
|
||||
b11011100 %
|
||||
b10111010 &
|
||||
b10011000 '
|
||||
b1110110 (
|
||||
b1010100 )
|
||||
b110010 *
|
||||
b10000 +
|
||||
#6500000
|
||||
#6750000
|
||||
0#
|
||||
|
|
@ -562,6 +562,14 @@ b1000110 ("
|
|||
b10001010 0"
|
||||
b11001110 8"
|
||||
1#
|
||||
b0 $
|
||||
b0 %
|
||||
b0 &
|
||||
b0 '
|
||||
b0 (
|
||||
b0 )
|
||||
b0 *
|
||||
b0 +
|
||||
1.
|
||||
1A
|
||||
b0 B
|
||||
|
|
@ -573,14 +581,6 @@ b0 G
|
|||
b0 H
|
||||
b0 I
|
||||
1L
|
||||
b0 $
|
||||
b0 %
|
||||
b0 &
|
||||
b0 '
|
||||
b0 (
|
||||
b0 )
|
||||
b0 *
|
||||
b0 +
|
||||
#7500000
|
||||
#7750000
|
||||
0#
|
||||
|
|
@ -688,6 +688,14 @@ b1 !
|
|||
b1 ?
|
||||
#10250000
|
||||
1#
|
||||
b11111110 $
|
||||
b11011100 %
|
||||
b10111010 &
|
||||
b10011000 '
|
||||
b1110110 (
|
||||
b1010100 )
|
||||
b110010 *
|
||||
b10000 +
|
||||
1.
|
||||
1A
|
||||
b11111110 B
|
||||
|
|
@ -699,14 +707,6 @@ b1010100 G
|
|||
b110010 H
|
||||
b10000 I
|
||||
1L
|
||||
b11111110 $
|
||||
b11011100 %
|
||||
b10111010 &
|
||||
b10011000 '
|
||||
b1110110 (
|
||||
b1010100 )
|
||||
b110010 *
|
||||
b10000 +
|
||||
#10500000
|
||||
#10750000
|
||||
0#
|
||||
|
|
@ -718,6 +718,14 @@ b10 !
|
|||
b10 ?
|
||||
#11250000
|
||||
1#
|
||||
b10011 $
|
||||
b1010111 %
|
||||
b10011011 &
|
||||
b11011111 '
|
||||
b10 (
|
||||
b1000110 )
|
||||
b10001010 *
|
||||
b11001110 +
|
||||
1.
|
||||
1A
|
||||
b10011 B
|
||||
|
|
@ -729,14 +737,6 @@ b1000110 G
|
|||
b10001010 H
|
||||
b11001110 I
|
||||
1L
|
||||
b10011 $
|
||||
b1010111 %
|
||||
b10011011 &
|
||||
b11011111 '
|
||||
b10 (
|
||||
b1000110 )
|
||||
b10001010 *
|
||||
b11001110 +
|
||||
#11500000
|
||||
#11750000
|
||||
0#
|
||||
|
|
@ -748,6 +748,14 @@ b11 !
|
|||
b11 ?
|
||||
#12250000
|
||||
1#
|
||||
b1110100 $
|
||||
b1100101 %
|
||||
b1110011 &
|
||||
b1110100 '
|
||||
b1101001 (
|
||||
b1101110 )
|
||||
b1100111 *
|
||||
b100001 +
|
||||
1.
|
||||
1A
|
||||
b1110100 B
|
||||
|
|
@ -759,14 +767,6 @@ b1101110 G
|
|||
b1100111 H
|
||||
b100001 I
|
||||
1L
|
||||
b1110100 $
|
||||
b1100101 %
|
||||
b1110011 &
|
||||
b1110100 '
|
||||
b1101001 (
|
||||
b1101110 )
|
||||
b1100111 *
|
||||
b100001 +
|
||||
#12500000
|
||||
#12750000
|
||||
0#
|
||||
|
|
@ -780,6 +780,14 @@ b0 ?
|
|||
0@
|
||||
#13250000
|
||||
1#
|
||||
b1101101 $
|
||||
b1101111 %
|
||||
b1110010 &
|
||||
b1100101 '
|
||||
b100000 (
|
||||
b1110100 )
|
||||
b1110011 *
|
||||
b1110100 +
|
||||
1.
|
||||
1A
|
||||
b1101101 B
|
||||
|
|
@ -791,14 +799,6 @@ b1110100 G
|
|||
b1110011 H
|
||||
b1110100 I
|
||||
1L
|
||||
b1101101 $
|
||||
b1101111 %
|
||||
b1110010 &
|
||||
b1100101 '
|
||||
b100000 (
|
||||
b1110100 )
|
||||
b1110011 *
|
||||
b1110100 +
|
||||
#13500000
|
||||
#13750000
|
||||
0#
|
||||
|
|
@ -808,6 +808,14 @@ b1110100 +
|
|||
#14000000
|
||||
#14250000
|
||||
1#
|
||||
b0 $
|
||||
b0 %
|
||||
b0 &
|
||||
b0 '
|
||||
b0 (
|
||||
b0 )
|
||||
b0 *
|
||||
b0 +
|
||||
1.
|
||||
1A
|
||||
b0 B
|
||||
|
|
@ -819,14 +827,6 @@ b0 G
|
|||
b0 H
|
||||
b0 I
|
||||
1L
|
||||
b0 $
|
||||
b0 %
|
||||
b0 &
|
||||
b0 '
|
||||
b0 (
|
||||
b0 )
|
||||
b0 *
|
||||
b0 +
|
||||
#14500000
|
||||
#14750000
|
||||
0#
|
||||
|
|
|
|||
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