1
0
Fork 0

Compare commits

..

84 commits

Author SHA1 Message Date
26224abe1c
sim: properly update all VCD wires when they share simulation state 2026-05-05 21:12:00 -07:00
2266315944
redo #[hdl(sim)] match/let destructuring to support matching values of type Type::SimValue 2026-05-03 23:23:17 -07:00
7e9d7739fb
use #[hdl(cmp_eq)] for HdlOption and implement conversion <-> Option 2026-05-01 18:46:36 -07:00
7516ec3c24
implement #[hdl(cmp_eq)] for enums 2026-05-01 18:34:49 -07:00
8e4eeef723
add support for custom debug/display formatting of #[hdl] structs/enums
also cleans up default debug formatting to use the struct/enum name
(or MaskType<StructName>) instead of the implementation detail type name.
2026-04-30 23:10:49 -07:00
402f457c68
sim: Speed up updating traces by tracking which traces are written to 2026-04-30 19:12:20 -07:00
8cff3687f7
Run Rocq tests. 2026-03-30 19:36:24 -03:00
80b92c7dd3
change vcd output to have module contents under instance's name, more closely matching how it works in verilog 2026-03-26 18:21:14 -07:00
2aa41137d4
add simulator tests for queue() 2026-03-24 23:30:15 -07:00
a0b2dc085c
add test that simulator handles last-connect semantics properly 2026-03-24 23:29:30 -07:00
a8a541b357
sim/compiler: fix registers so they properly retain their old value when not written 2026-03-24 23:26:47 -07:00
52c41bb5db
display signals when panicking because not all inputs/outputs are written yet 2026-03-24 23:25:14 -07:00
a93e66d8ab
update ui test's expected output for having rust-src available 2026-03-17 20:43:46 -07:00
eb3ca59053
add rust-src to CI 2026-03-17 20:42:54 -07:00
dbed947408
change VCD id generation to be based on hashing the path, making them better for git diff 2026-02-23 20:05:10 -08:00
cb4e1f42c0
silence unused import warning 2026-02-23 16:07:46 -08:00
8c270b0e35
silence warning for enums with only one variant 2026-02-23 16:07:05 -08:00
c632e5d570
speed up simulation by optimizing SimulationImpl::read_traces
this makes cpu/crates/cpu/tests/next_pc.rs take 56s instead of 168s
2026-02-04 15:41:09 -08:00
1bc835803b
speed up LazyInterned by redoing caching using RwLock and add a thread-local cache 2026-02-03 18:00:36 -08:00
9db3240644
fix UI test's expected output 2026-02-03 18:00:36 -08:00
caa097db0b
change rust version to 1.93.0 2026-02-03 17:29:59 -08:00
a96efa9696
cache interned UInt/SInt types 2026-02-02 17:51:30 -08:00
4ac1bcbc0a
change Interner to use a sharded hash table 2026-02-02 15:49:26 -08:00
39810043ea
move Interner into new mod interner 2026-02-02 15:44:37 -08:00
26b0dc3fd8
change Interner to pub(crate) 2026-02-02 15:42:12 -08:00
11281a9842
add a thread-local cache when using TypeIdMap 2026-02-01 21:19:32 -08:00
e366793204
don't compare function pointers -- they're non-deterministic 2026-01-12 03:11:36 -08:00
a398f8f185
Define design safety, and prove it for 1-step and 2-step induction. 2025-12-22 22:30:13 -03:00
4fd4371054
Spelling. 2025-12-22 22:30:06 -03:00
c97b44d9d6
simplify SimValue Debug format, making complex structures much easier to read 2025-12-14 20:59:48 -08:00
fbe4585578
add FillInDefaultedGenerics<Type = Self> bound for SizeType 2025-12-10 20:10:39 -08:00
e4210a672f
Check copyright header in Rocq files.
If we ever add Verilog files, we can "or" both results, I guess.
2025-12-09 07:45:35 -03:00
e54558d848
Demonstrates state with multiple variables and hidden state. 2025-12-08 22:00:49 -03:00
46f3519c76
Demonstrate a preliminary mapping from HDL to Rocq.
Starts with a very simple example, including a proof by induction.
2025-12-08 19:32:05 -03:00
9e803223d0
support operations directly on SimValue, UIntValue, and SIntValue, and shared references to those 2025-11-24 00:14:53 -08:00
2a65aa2bd5
fix DynShr[SU]'s literal bits to properly shift right instead of left 2025-11-20 02:25:05 -08:00
2817cd3d58
support Rust's default binding modes when destructuring with #[hdl(sim)] let/match 2025-11-14 00:20:54 -08:00
053c1b2b10
implement Display for SimValue<T> 2025-11-14 00:20:54 -08:00
17b58e8edb
add utility impls for SimValue<ArrayType<_, _>> 2025-11-13 20:21:07 -08:00
df020e9c9b
add ExternModuleSimulatorState::read_past() and more output when simulator trace is enabled 2025-11-12 22:31:45 -08:00
45fea70c18
add ExternModuleSimulationState::fork_join_scope 2025-11-07 02:18:43 -08:00
fbc8ffa5ae
fix private fields in #[hdl] pub struct 2025-11-06 20:23:16 -08:00
26a7090178
add ParsedVisibility 2025-11-06 20:22:53 -08:00
0b77d1bea0
fix Simulator panicking when you use PhantomConst 2025-11-05 22:44:43 -08:00
840c5e1895
add ExternModuleSimulationState::resettable helper for procedural simulations that have a reset input. 2025-11-03 23:59:36 -08:00
c11a1743f9
add sim.fork_join() and fix Simulator to handle running futures with arbitrary wakers 2025-10-30 21:16:05 -07:00
0be9f9ce23
fix JobGraph::run to not busy-wait 2025-10-27 22:57:12 -07:00
0b82178740
add PhantomConstGet to the known Type bounds for #[hdl] struct/enum 2025-10-27 20:08:22 -07:00
4b24a88641
add docs for #[hdl] and particularly for #[hdl] type aliases 2025-10-26 03:25:35 -07:00
094c77e26e
add #[hdl(get(|v| ...))] type GetStuff<P: PhantomConstGet<MyStruct>> = MyType or DynSize; 2025-10-26 03:25:35 -07:00
d2c8b023bf
deny broken docs 2025-10-26 03:25:35 -07:00
c043ee54d0
fix rustdoc warning for link in readme 2025-10-26 03:25:35 -07:00
edcc5927a5
don't cache external job failures if they could be caused by the user killing processes 2025-10-24 02:27:20 -07:00
7dc4417874
add test_many_memories so we catch if memories are iterated in an inconsistent order like in 838bd469ce 2025-10-24 01:40:30 -07:00
838bd469ce
change SimulationImpl::trace_memories to a BTreeMap for consistent iteration order 2025-10-24 00:53:13 -07:00
b6e4cd0614
move FormalMode to crate::testing and add to prelude 2025-10-24 00:14:04 -07:00
3e5b2f126a
make UIntInRange[Inclusive][Type] castable from/to any UInt<N> and add methods to get bit_width, start, and end 2025-10-23 23:52:41 -07:00
040cefea21
add tx_only_uart example to readme 2025-10-22 20:31:25 -07:00
3267cb38c4
build tx_only_uart in CI 2025-10-22 20:12:08 -07:00
b3cc28e2b6
add transmit-only UART example 2025-10-22 20:11:02 -07:00
26840daf13
arty_a7: add divided clocks as available input peripherals so you're not stuck with 100MHz 2025-10-22 20:11:02 -07:00
4d9e8d3b47
Add building blinky example to the readme 2025-10-21 23:00:16 -07:00
c6feea6d51
properly handle all XilinxAnnotations, this makes nextpnr-xilinx properly pick up the clock frequency 2025-10-21 22:24:02 -07:00
409992961c
switch to using verilog for reset synchronizer so we can use attributes on FDPE instances 2025-10-21 22:24:02 -07:00
2bdc8a7c72
WIP adding xdc create_clock -- nextpnr-xilinx currently ignores it 2025-10-19 23:13:28 -07:00
477a1f2d29
Add peripherals and Arty A7 platforms -- blinky works correctly now on arty-a7-100t! 2025-10-19 23:13:28 -07:00
4d54f903be
move vendor module to top level 2025-10-17 15:00:19 -07:00
3f5dd61e46
WIP adding Platform 2025-10-17 05:55:22 -07:00
def406ab52
group all xilinx annotations together 2025-10-16 04:53:58 -07:00
a565be1b09
do some clean up 2025-10-16 04:32:56 -07:00
676c1e3b7d
WIP adding annotations for generating the .xdc file for yosys-nextpnr-prjxray 2025-10-15 04:29:00 -07:00
169be960f8
generate Arty A7 100T .bit file for blinky example in CI 2025-10-15 04:29:00 -07:00
2b52799f5c
try building .bit file 2025-10-15 04:29:00 -07:00
35f98f3229
fix redirects 2025-10-15 04:29:00 -07:00
8a63ea89d0
WIP adding yosys-nextpnr-xray xilinx fpga toolchain -- blinky works on arty a7 100t (except for inverted reset) 2025-10-15 04:29:00 -07:00
84c5978eaf
WIP build Xilinx FPGA dependencies in CI 2025-10-15 04:29:00 -07:00
42e3179a60
change cache directory name to be fayalite-specific 2025-10-15 04:29:00 -07:00
53ae3ff670
mark create-unix-shell-script as incomplete in CLI 2025-10-15 04:29:00 -07:00
7af9abfb6f
switch to using new crate::build system 2025-10-15 04:29:00 -07:00
aacd05378f
WIP converting from cli.rs to build/*.rs 2025-10-15 04:29:00 -07:00
908ccef674
added automatically-added dependencies; added caching for external jobs 2025-10-15 04:29:00 -07:00
057670c12a
WIP adding FPGA support -- build module should be complete 2025-10-15 04:29:00 -07:00
f8ac78abd6
Remove extraneous #[automatically_derived] annotations that are causing warnings as reported by Tobias 2025-10-15 04:17:47 -07:00
64ec6c0dcc
switch to use server's new actions org 2025-10-09 23:48:17 -07:00
178 changed files with 128417 additions and 8588 deletions

View file

@ -1,77 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# See Notices.txt for copyright information
on:
workflow_call:
outputs:
cache-primary-key:
value: ${{ jobs.deps.outputs.cache-primary-key }}
jobs:
deps:
runs-on: debian-12
outputs:
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
steps:
- uses: https://git.libre-chip.org/mirrors/checkout@v3
with:
fetch-depth: 0
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
id: restore-deps
with:
path: deps
key: ${{ github.repository }}-deps-${{ runner.os }}-${{ hashFiles('.forgejo/workflows/deps.yml') }}
lookup-only: true
- name: Install Apt packages
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
apt-get update -qq
apt-get install -qq \
bison \
build-essential \
ccache \
clang \
cvc5 \
flex \
gawk \
g++ \
git \
libboost-filesystem-dev \
libboost-python-dev \
libboost-system-dev \
libffi-dev \
libreadline-dev \
lld \
pkg-config \
python3 \
python3-click \
tcl-dev \
zlib1g-dev
- name: Install Firtool
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
mkdir -p deps
wget -O deps/firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz
sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 deps/firrtl.tar.gz'
tar -C deps -xvaf deps/firrtl.tar.gz
rm -rf deps/firtool
mv deps/firtool-1.86.0 deps/firtool
- name: Get SymbiYosys
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby
- name: Build Z3
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
make -C deps/z3/build -j"$(nproc)"
- name: Build Yosys
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys
make -C deps/yosys -j"$(nproc)"
- uses: https://git.libre-chip.org/mirrors/cache/save@v3
if: steps.restore-deps.outputs.cache-hit != 'true'
with:
path: deps
key: ${{ steps.restore-deps.outputs.cache-primary-key }}

View file

@ -3,62 +3,26 @@
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' }}
- run: rustup override set 1.93.0
- run: rustup component add rust-src
- run: make -C rocq-demo
- run: cargo test
- run: cargo build --tests --features=unstable-doc
- 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

134
Cargo.lock generated
View file

@ -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,10 +319,12 @@ dependencies = [
"jobslot",
"num-bigint",
"num-traits",
"os_pipe",
"once_cell",
"ordered-float",
"petgraph",
"serde",
"serde_json",
"sha2",
"tempfile",
"trybuild",
"vec_map",
@ -377,12 +396,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 +469,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"
@ -503,18 +523,19 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.19.0"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[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 +578,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 +794,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 +853,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 +870,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 +947,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"

View file

@ -11,24 +11,27 @@ edition = "2024"
repository = "https://git.libre-chip.org/libre-chip/fayalite"
keywords = ["hdl", "hardware", "semiconductors", "firrtl", "fpga"]
categories = ["simulation", "development-tools", "compilers"]
rust-version = "1.89.0"
rust-version = "1.93.0"
[workspace.dependencies]
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"
once_cell = "1.21.3"
ordered-float = { version = "5.1.0", features = ["serde"] }
petgraph = "0.8.1"
prettyplease = "0.2.20"
proc-macro2 = "1.0.83"

View file

@ -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

View file

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

View file

@ -3,8 +3,9 @@
use crate::{
Errors, HdlAttr, PairsIterExt,
hdl_type_common::{
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, ParsedFieldsNamed, ParsedGenerics,
SplitForImpl, TypesParser, WrappedInConst, common_derives, get_target,
CustomDebugOptions, CustomDebugTrait, ItemOptions, MakeHdlTypeExpr, MaybeParsed,
ParsedField, ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst,
common_derives, create_struct_debug_impl, get_target,
},
kw,
};
@ -30,6 +31,7 @@ pub(crate) struct ParsedBundle {
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>,
pub(crate) mask_type_ident: Ident,
pub(crate) mask_type_name: String,
pub(crate) mask_type_match_variant_ident: Ident,
pub(crate) mask_type_sim_value_ident: Ident,
pub(crate) match_variant_ident: Ident,
@ -87,7 +89,13 @@ impl ParsedBundle {
no_static: _,
no_runtime_generics: _,
cmp_eq: _,
ref get,
custom_debug: _,
custom_sim_display: _,
} = 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) => {
@ -127,6 +135,7 @@ impl ParsedBundle {
fields,
field_flips,
mask_type_ident: format_ident!("__{}__MaskType", ident),
mask_type_name: format!("MaskType<{}>", ident),
mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident),
mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident),
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
@ -220,7 +229,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 +354,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 +388,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 +400,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),)*
@ -431,6 +453,7 @@ impl ToTokens for ParsedBundle {
fields,
field_flips,
mask_type_ident,
mask_type_name,
mask_type_match_variant_ident,
mask_type_sim_value_ident,
match_variant_ident,
@ -446,11 +469,21 @@ impl ToTokens for ParsedBundle {
no_static,
no_runtime_generics,
cmp_eq,
get: _,
custom_debug: _,
custom_sim_display,
} = &options.body;
let CustomDebugOptions {
type_: custom_debug_type,
sim: custom_debug_sim,
mask_type: custom_debug_mask_type,
mask_sim: custom_debug_mask_sim,
} = options.body.custom_debug();
let target = get_target(target, ident);
let struct_name = ident.to_string();
let mut item_attrs = attrs.clone();
item_attrs.push(common_derives(span));
ItemStruct {
item_attrs.push(common_derives(span, false));
let type_struct = ItemStruct {
attrs: item_attrs,
vis: vis.clone(),
struct_token: *struct_token,
@ -458,8 +491,8 @@ impl ToTokens for ParsedBundle {
generics: generics.into(),
fields: Fields::Named(fields.clone().into()),
semi_token: None,
}
.to_tokens(tokens);
};
type_struct.to_tokens(tokens);
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields), None) =
(generics, fields, no_runtime_generics)
@ -485,6 +518,9 @@ impl ToTokens for ParsedBundle {
}
let mut wrapped_in_const = WrappedInConst::new(tokens, span);
let tokens = wrapped_in_const.inner();
if custom_debug_type.is_none() {
create_struct_debug_impl(&type_struct, &struct_name, None).to_tokens(tokens);
}
let builder = Builder {
vis: vis.clone(),
struct_token: *struct_token,
@ -495,7 +531,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,11 +548,9 @@ 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 {
let mask_type_struct = ItemStruct {
attrs: vec![
common_derives(span),
common_derives(span, false),
parse_quote_spanned! {span=>
#[allow(non_camel_case_types, dead_code)]
},
@ -528,17 +561,20 @@ impl ToTokens for ParsedBundle {
generics: generics.into(),
fields: Fields::Named(mask_type_fields.clone()),
semi_token: None,
};
mask_type_struct.to_tokens(tokens);
if custom_debug_mask_type.is_none() {
create_struct_debug_impl(&mask_type_struct, mask_type_name, None).to_tokens(tokens);
}
.to_tokens(tokens);
let mut mask_type_match_variant_fields = mask_type_fields.clone();
for Field { ty, .. } in &mut mask_type_match_variant_fields.named {
*ty = parse_quote_spanned! {span=>
::fayalite::expr::Expr<#ty>
};
}
ItemStruct {
let mask_type_match_variant_struct = ItemStruct {
attrs: vec![
common_derives(span),
common_derives(span, false),
parse_quote_spanned! {span=>
#[allow(non_camel_case_types, dead_code)]
},
@ -549,17 +585,19 @@ impl ToTokens for ParsedBundle {
generics: generics.into(),
fields: Fields::Named(mask_type_match_variant_fields),
semi_token: None,
}
.to_tokens(tokens);
};
mask_type_match_variant_struct.to_tokens(tokens);
create_struct_debug_impl(&mask_type_match_variant_struct, mask_type_name, None)
.to_tokens(tokens);
let mut match_variant_fields = FieldsNamed::from(fields.clone());
for Field { ty, .. } in &mut match_variant_fields.named {
*ty = parse_quote_spanned! {span=>
::fayalite::expr::Expr<#ty>
};
}
ItemStruct {
let match_variant_struct = ItemStruct {
attrs: vec![
common_derives(span),
common_derives(span, false),
parse_quote_spanned! {span=>
#[allow(non_camel_case_types, dead_code)]
},
@ -570,19 +608,19 @@ impl ToTokens for ParsedBundle {
generics: generics.into(),
fields: Fields::Named(match_variant_fields),
semi_token: None,
}
.to_tokens(tokens);
};
match_variant_struct.to_tokens(tokens);
create_struct_debug_impl(&match_variant_struct, &struct_name, None).to_tokens(tokens);
let mut mask_type_sim_value_fields = mask_type_fields;
for Field { ty, .. } in &mut mask_type_sim_value_fields.named {
*ty = parse_quote_spanned! {span=>
::fayalite::sim::value::SimValue<#ty>
};
}
ItemStruct {
let mask_type_sim_value_struct = ItemStruct {
attrs: vec![
parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone,
)]
},
@ -596,19 +634,34 @@ impl ToTokens for ParsedBundle {
generics: generics.into(),
fields: Fields::Named(mask_type_sim_value_fields),
semi_token: None,
};
mask_type_sim_value_struct.to_tokens(tokens);
if custom_debug_mask_sim.is_none() {
create_struct_debug_impl(
&mask_type_struct,
mask_type_name,
Some(CustomDebugTrait {
trait_path: &parse_quote_spanned! {span=>
::fayalite::ty::SimValueDebug
},
fn_name: &format_ident!("sim_value_debug", span = span),
this_arg: &parse_quote_spanned! {span=>
value: &<Self as ::fayalite::ty::Type>::SimValue
},
}),
)
.to_tokens(tokens);
}
.to_tokens(tokens);
let mut sim_value_fields = FieldsNamed::from(fields.clone());
for Field { ty, .. } in &mut sim_value_fields.named {
*ty = parse_quote_spanned! {span=>
::fayalite::sim::value::SimValue<#ty>
};
}
ItemStruct {
let sim_value_struct = ItemStruct {
attrs: vec![
parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone,
)]
},
@ -622,8 +675,36 @@ impl ToTokens for ParsedBundle {
generics: generics.into(),
fields: Fields::Named(sim_value_fields),
semi_token: None,
};
sim_value_struct.to_tokens(tokens);
if custom_debug_sim.is_none() {
create_struct_debug_impl(
&type_struct,
&struct_name,
Some(CustomDebugTrait {
trait_path: &parse_quote_spanned! {span=>
::fayalite::ty::SimValueDebug
},
fn_name: &format_ident!("sim_value_debug", span = span),
this_arg: &parse_quote_spanned! {span=>
value: &<Self as ::fayalite::ty::Type>::SimValue
},
}),
)
.to_tokens(tokens);
}
if custom_sim_display.is_some() {
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics
#where_clause
{
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
<#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(self, f)
}
}
}.to_tokens(tokens);
}
.to_tokens(tokens);
let this_token = Ident::new("__this", span);
let fields_token = Ident::new("__fields", span);
let self_token = Token![self](span);
@ -694,10 +775,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 +862,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 +886,47 @@ 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::__std::fmt::Debug for #mask_type_sim_value_ident #type_generics
#where_clause
{
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
<#mask_type_ident #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::expr::ValueType for #mask_type_sim_value_ident #type_generics
#where_clause
{
type Type = #mask_type_ident #type_generics;
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
#mask_type_ident {
#(#value_type_fields)*
}
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics
#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 +1030,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 +1054,47 @@ impl ToTokens for ParsedBundle {
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics
#where_clause
{
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
<#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::expr::ValueType for #sim_value_ident #type_generics
#where_clause
{
type Type = #target #type_generics;
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
#target {
#(#value_type_fields)*
}
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
#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 +1120,172 @@ impl ToTokens for ParsedBundle {
}
.to_tokens(tokens);
if let Some((cmp_eq,)) = cmp_eq {
let mut expr_where_clause =
let mut cmp_eq_where_clause =
Generics::from(generics)
.where_clause
.unwrap_or_else(|| syn::WhereClause {
where_token: Token![where](span),
predicates: Punctuated::new(),
});
let mut sim_value_where_clause = expr_where_clause.clone();
let mut fields_sim_value_eq = vec![];
let mut fields_cmp_eq = vec![];
let mut fields_cmp_ne = vec![];
let mut fields_value_eq = vec![];
let mut fields_value_ne = vec![];
let mut fields_expr_eq = vec![];
let mut fields_expr_ne = vec![];
let mut fields_valueless_eq = vec![];
let mut fields_valueless_ne = vec![];
for field in fields.named() {
let field_ident = field.ident();
let field_ty = field.ty();
expr_where_clause
cmp_eq_where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
#field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty>
});
sim_value_where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty>
});
fields_sim_value_eq.push(quote_spanned! {span=>
::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident)
fields_value_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_value_eq(
__lhs.#field_ident,
::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident),
__rhs.#field_ident,
::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident),
)
});
fields_cmp_eq.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
fields_value_ne.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_value_ne(
__lhs.#field_ident,
::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident),
__rhs.#field_ident,
::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident),
)
});
fields_cmp_ne.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
fields_expr_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(
__lhs.#field_ident,
__rhs.#field_ident,
)
});
fields_expr_ne.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_expr_ne(
__lhs.#field_ident,
__rhs.#field_ident,
)
});
fields_valueless_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq(
::fayalite::expr::Valueless::new(__lhs.#field_ident),
::fayalite::expr::Valueless::new(__rhs.#field_ident),
)
});
fields_valueless_ne.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_ne(
::fayalite::expr::Valueless::new(__lhs.#field_ident),
::fayalite::expr::Valueless::new(__rhs.#field_ident),
)
});
}
let sim_value_eq_body;
let cmp_eq_body;
let cmp_ne_body;
let value_eq_body;
let value_ne_body;
let expr_eq_body;
let expr_ne_body;
let valueless_eq_body;
let valueless_ne_body;
if fields_len == 0 {
sim_value_eq_body = quote_spanned! {span=>
value_eq_body = quote_spanned! {span=>
true
};
cmp_eq_body = quote_spanned! {span=>
value_ne_body = quote_spanned! {span=>
false
};
expr_eq_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&true)
};
cmp_ne_body = quote_spanned! {span=>
expr_ne_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&false)
};
valueless_eq_body = quote_spanned! {span=>
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
};
valueless_ne_body = quote_spanned! {span=>
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
};
} else {
sim_value_eq_body = quote_spanned! {span=>
#(#fields_sim_value_eq)&&*
value_eq_body = quote_spanned! {span=>
#(#fields_value_eq)&*
};
cmp_eq_body = quote_spanned! {span=>
#(#fields_cmp_eq)&*
value_ne_body = quote_spanned! {span=>
#(#fields_value_ne)|*
};
cmp_ne_body = quote_spanned! {span=>
#(#fields_cmp_ne)|*
expr_eq_body = quote_spanned! {span=>
#(#fields_expr_eq)&*
};
expr_ne_body = quote_spanned! {span=>
#(#fields_expr_ne)|*
};
valueless_eq_body = quote_spanned! {span=>
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
#(#fields_valueless_eq)&*
};
valueless_ne_body = quote_spanned! {span=>
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
#(#fields_valueless_ne)|*
};
};
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<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
}
}
#[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_eq(
__lhs: ::fayalite::expr::Valueless<Self>,
__rhs: ::fayalite::expr::Valueless<Self>,
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
#valueless_eq_body
}
#[track_caller]
fn cmp_valueless_ne(
__lhs: ::fayalite::expr::Valueless<Self>,
__rhs: ::fayalite::expr::Valueless<Self>,
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
#valueless_ne_body
}
}
}

View file

@ -3,8 +3,9 @@
use crate::{
Errors, HdlAttr, PairsIterExt,
hdl_type_common::{
ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, SplitForImpl,
TypesParser, WrappedInConst, common_derives, get_target,
CustomDebugOptions, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType,
SplitForImpl, TypesParser, WrappedInConst, common_derives, create_struct_debug_impl,
get_target,
},
kw,
};
@ -158,10 +159,31 @@ impl ParsedEnum {
custom_bounds,
no_static: _,
no_runtime_generics: _,
cmp_eq,
cmp_eq: _,
ref get,
custom_debug: _,
custom_sim_display: _,
} = 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");
}
let CustomDebugOptions {
type_: _,
sim: _,
mask_type,
mask_sim,
} = options.body.custom_debug();
if let Some((mask_type,)) = mask_type {
errors.error(
mask_type,
"#[hdl(custom_debug(mask_type)] is not allowed on enums",
);
}
if let Some((mask_sim,)) = mask_sim {
errors.error(
mask_sim,
"#[hdl(custom_debug(mask_sim)] is not allowed on enums",
);
}
attrs.retain(|attr| {
if attr.path().is_ident("repr") {
@ -224,11 +246,21 @@ impl ToTokens for ParsedEnum {
custom_bounds: _,
no_static,
no_runtime_generics,
cmp_eq: _, // TODO: implement cmp_eq for enums
cmp_eq,
get: _,
custom_debug: _,
custom_sim_display,
} = &options.body;
let CustomDebugOptions {
type_: custom_debug_type,
sim: custom_debug_sim,
mask_type: _,
mask_sim: _,
} = options.body.custom_debug();
let target = get_target(target, ident);
let enum_name = ident.to_string();
let mut struct_attrs = attrs.clone();
struct_attrs.push(common_derives(span));
struct_attrs.push(common_derives(span, false));
struct_attrs.push(parse_quote_spanned! {span=>
#[allow(non_snake_case)]
});
@ -268,7 +300,7 @@ impl ToTokens for ParsedEnum {
}
},
));
ItemStruct {
let type_struct = ItemStruct {
attrs: struct_attrs,
vis: vis.clone(),
struct_token: Token![struct](enum_token.span),
@ -283,8 +315,8 @@ impl ToTokens for ParsedEnum {
})
},
semi_token: None,
}
.to_tokens(tokens);
};
type_struct.to_tokens(tokens);
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
if let (MaybeParsed::Parsed(generics), None) = (generics, no_runtime_generics) {
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
@ -368,6 +400,9 @@ impl ToTokens for ParsedEnum {
}
.to_tokens(tokens);
}
if custom_debug_type.is_none() {
create_struct_debug_impl(&type_struct, &enum_name, None).to_tokens(tokens);
}
let mut enum_attrs = attrs.clone();
enum_attrs.push(parse_quote_spanned! {span=>
#[allow(dead_code, non_camel_case_types)]
@ -448,7 +483,6 @@ impl ToTokens for ParsedEnum {
let mut enum_attrs = attrs.clone();
enum_attrs.push(parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone,
)]
});
@ -549,7 +583,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 +604,6 @@ impl ToTokens for ParsedEnum {
)
}
}
#[automatically_derived]
impl #impl_generics #sim_builder_ident #type_generics
#where_clause
{
@ -593,7 +625,6 @@ impl ToTokens for ParsedEnum {
}
} else {
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics #target #type_generics
#where_clause
{
@ -608,7 +639,6 @@ impl ToTokens for ParsedEnum {
)
}
}
#[automatically_derived]
impl #impl_generics #sim_builder_ident #type_generics
#where_clause
{
@ -837,6 +867,240 @@ impl ToTokens for ParsedEnum {
},
)),
);
if custom_debug_sim.is_none() {
let debug_match_arms = Vec::from_iter(
variants
.iter()
.map(
|ParsedVariant {
attrs: _,
options: _,
ident,
field,
}| {
let variant_name = ident.to_string();
if let Some(_) = field {
quote_spanned! {span=>
#sim_value_ident::#ident(field, _) => {
f.debug_tuple(#variant_name).field(field).finish()
}
}
} else {
quote_spanned! {span=>
#sim_value_ident::#ident(_) => {
f.write_str(#variant_name)
}
}
}
},
)
.chain(sim_value_unknown_variant_name.as_ref().map(
|sim_value_unknown_variant_name| {
let sim_value_unknown_variant_name_str =
sim_value_unknown_variant_name.to_string();
quote_spanned! {span=>
#sim_value_ident::#sim_value_unknown_variant_name(_) => {
f.write_str(#sim_value_unknown_variant_name_str)
}
}
},
)),
);
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::ty::SimValueDebug for #target #type_generics
#where_clause
{
fn sim_value_debug(
value: &<Self as ::fayalite::ty::Type>::SimValue,
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
) -> ::fayalite::__std::fmt::Result {
match value {
#(#debug_match_arms)*
}
}
}
}
.to_tokens(tokens);
}
if custom_sim_display.is_some() {
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics
#where_clause
{
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
<#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(self, f)
}
}
}.to_tokens(tokens);
}
if let Some((cmp_eq,)) = cmp_eq {
let mut cmp_eq_where_clause =
Generics::from(generics)
.where_clause
.unwrap_or_else(|| syn::WhereClause {
where_token: Token![where](span),
predicates: Punctuated::new(),
});
let mut variants_value_eq = vec![];
let mut variants_expr_eq = vec![];
let mut fields_valueless_eq = vec![];
for (
variant_index,
ParsedVariant {
attrs: _,
options: variant_options,
ident: variant_ident,
field,
},
) in variants.iter().enumerate()
{
let VariantOptions {} = variant_options.body;
if let Some(ParsedVariantField {
paren_token: _,
attrs: _,
options: field_options,
ty: field_ty,
comma_token: _,
}) = field
{
let FieldOptions {} = field_options.body;
cmp_eq_where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty>
});
variants_value_eq.push(quote_spanned! {span=>
(#sim_value_ident::#variant_ident(__lhs_field, _), #sim_value_ident::#variant_ident(__rhs_field, _)) => {
::fayalite::expr::HdlPartialEqImpl::cmp_value_eq(
__lhs.#variant_ident,
::fayalite::__std::borrow::Cow::Borrowed(__lhs_field),
__rhs.#variant_ident,
::fayalite::__std::borrow::Cow::Borrowed(__rhs_field),
)
}
});
variants_expr_eq.push(quote_spanned! {span=>
{
let (#match_variant_ident::#variant_ident(__lhs), __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
::fayalite::__std::iter::Iterator::next(&mut __lhs_match_variant_iter)
.expect("known to have enough variants"),
)
else {
::fayalite::__std::unreachable!();
};
let (#match_variant_ident::#variant_ident(__rhs), __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
::fayalite::__std::iter::Iterator::nth(
&mut ::fayalite::module::match_(__rhs),
#variant_index,
)
.expect("known to have variant"),
)
else {
::fayalite::__std::unreachable!();
};
::fayalite::module::connect(__retval, ::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(__lhs, __rhs));
}
});
fields_valueless_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq(
::fayalite::expr::Valueless::new(__lhs.#variant_ident),
::fayalite::expr::Valueless::new(__rhs.#variant_ident),
)
});
} else {
variants_value_eq.push(quote_spanned! {span=>
(#sim_value_ident::#variant_ident(_), #sim_value_ident::#variant_ident(_)) => true,
});
variants_expr_eq.push(quote_spanned! {span=>
{
let (#match_variant_ident::#variant_ident, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
::fayalite::__std::iter::Iterator::next(&mut __lhs_match_variant_iter)
.expect("known to have enough variants"),
)
else {
::fayalite::__std::unreachable!();
};
let (#match_variant_ident::#variant_ident, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
::fayalite::__std::iter::Iterator::nth(
&mut ::fayalite::module::match_(__rhs),
#variant_index,
)
.expect("known to have variant"),
)
else {
::fayalite::__std::unreachable!();
};
::fayalite::module::connect(__retval, true);
}
});
}
}
if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name {
variants_value_eq.push(quote_spanned! {span=>
(#sim_value_ident::#sim_value_unknown_variant_name(__lhs_unknown), #sim_value_ident::#sim_value_unknown_variant_name(__rhs_unknown)) => {
__lhs_unknown == __rhs_unknown
}
});
}
let valueless_eq_body = if fields_valueless_eq.is_empty() {
quote_spanned! {span=>
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
}
} else {
quote_spanned! {span=>
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
#(#fields_valueless_eq)&*
}
};
let cmp_expr_eq_wire_name = format!("{ident}_cmp_eq");
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::expr::HdlPartialEqImpl<Self> for #target #type_generics
#cmp_eq_where_clause
{
#[track_caller]
fn cmp_value_eq(
__lhs: Self,
__lhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
__rhs: Self,
__rhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
) -> ::fayalite::__std::primitive::bool {
match (&*__lhs_value, &*__rhs_value) {
#(#variants_value_eq)*
_ => false,
}
}
#[track_caller]
fn cmp_expr_eq(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
let __retval = ::fayalite::module::wire(::fayalite::module::ImplicitName(#cmp_expr_eq_wire_name), ::fayalite::int::Bool);
::fayalite::module::connect(__retval, false);
let mut __lhs_match_variant_iter = ::fayalite::module::match_(__lhs);
#(#variants_expr_eq)*
__retval
}
#[track_caller]
fn cmp_valueless_eq(
__lhs: ::fayalite::expr::Valueless<Self>,
__rhs: ::fayalite::expr::Valueless<Self>,
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
#valueless_eq_body
}
}
}
.to_tokens(tokens);
}
let variants_len = variants.len();
quote_spanned! {span=>
#[automatically_derived]
@ -887,6 +1151,7 @@ impl ToTokens for ParsedEnum {
#(#sim_value_from_opaque_match_arms)*
}
}
#[allow(irrefutable_let_patterns)]
fn sim_value_clone_from_opaque(
&self,
value: &mut <Self as ::fayalite::ty::Type>::SimValue,
@ -932,6 +1197,14 @@ impl ToTokens for ParsedEnum {
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics
#where_clause
{
fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
<#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics>
for #sim_value_ident #type_generics
#where_clause
@ -1023,16 +1296,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 +1325,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,

View file

@ -3,29 +3,275 @@
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![:](ident.span()));
let mut bounds = bounds.into_pairs();
let (bound, plus_token) = bounds.next()?.into_tuple();
let phantom_const_get_bound = PhantomConstGetBound::parse_type_param_bound(bound)
.ok()?
.ok()?;
let None = bounds.next() else {
return None;
};
Some(Self {
attrs,
ident,
colon_token,
phantom_const_get_bound,
plus_token,
})
}
}
#[derive(Clone, Debug)]
pub(crate) struct PhantomConstAccessorGenerics {
lt_token: Token![<],
type_param: PhantomConstAccessorTypeParam,
comma_token: Option<Token![,]>,
gt_token: Token![>],
}
impl From<PhantomConstAccessorGenerics> for Generics {
fn from(value: PhantomConstAccessorGenerics) -> Self {
let PhantomConstAccessorGenerics {
lt_token,
type_param,
comma_token,
gt_token,
} = value;
Generics {
lt_token: Some(lt_token),
params: FromIterator::from_iter([Pair::new(type_param.into(), comma_token)]),
gt_token: Some(gt_token),
where_clause: None,
}
}
}
impl<'a> From<&'a PhantomConstAccessorGenerics> for Generics {
fn from(value: &'a PhantomConstAccessorGenerics) -> Self {
value.clone().into()
}
}
impl PhantomConstAccessorGenerics {
fn parse_opt(generics: Generics) -> Option<Self> {
let Generics {
lt_token,
params,
gt_token,
where_clause: None,
} = generics
else {
return None;
};
let mut params = params.into_pairs();
let (generic_param, comma_token) = params.next()?.into_tuple();
let type_param = PhantomConstAccessorTypeParam::parse_opt(generic_param)?;
let span = type_param.ident.span();
let lt_token = lt_token.unwrap_or(Token![<](span));
let gt_token = gt_token.unwrap_or(Token![>](span));
let None = params.next() else {
return None;
};
Some(Self {
lt_token,
type_param,
comma_token,
gt_token,
})
}
}
#[derive(Clone, Debug)]
pub(crate) enum ParsedTypeAlias {
TypeAlias {
attrs: Vec<Attribute>,
options: HdlAttr<ItemOptions, kw::hdl>,
vis: Visibility,
type_token: Token![type],
ident: Ident,
generics: MaybeParsed<ParsedGenerics, Generics>,
eq_token: Token![=],
ty: MaybeParsed<ParsedType, Type>,
semi_token: Token![;],
},
PhantomConstAccessor {
attrs: Vec<Attribute>,
options: HdlAttr<ItemOptions, kw::hdl>,
get: (kw::get, Paren, Expr),
vis: Visibility,
type_token: Token![type],
ident: Ident,
generics: PhantomConstAccessorGenerics,
eq_token: Token![=],
ty: Type,
ty_is_dyn_size: Option<known_items::DynSize>,
semi_token: Token![;],
},
}
impl ParsedTypeAlias {
fn ty_is_dyn_size(ty: &Type) -> Option<known_items::DynSize> {
match ty {
Type::Group(TypeGroup {
group_token: _,
elem,
}) => Self::ty_is_dyn_size(elem),
Type::Paren(TypeParen {
paren_token: _,
elem,
}) => Self::ty_is_dyn_size(elem),
Type::Path(syn::TypePath { qself: None, path }) => {
known_items::DynSize::parse_path(path.clone()).ok()
}
_ => None,
}
}
fn parse_phantom_const_accessor(
item: ItemType,
mut errors: Errors,
options: HdlAttr<ItemOptions, kw::hdl>,
get: (kw::get, Paren, Expr),
) -> syn::Result<Self> {
let ItemType {
attrs,
vis,
type_token,
ident,
generics,
eq_token,
ty,
semi_token,
} = item;
let ItemOptions {
outline_generated: _,
ref target,
custom_bounds,
no_static,
no_runtime_generics,
cmp_eq,
get: _,
ref custom_debug,
custom_sim_display,
} = options.body;
if let Some((no_static,)) = no_static {
errors.error(no_static, "no_static is not valid on type aliases");
}
if let Some((target, ..)) = target {
errors.error(
target,
"target is not implemented on PhantomConstGet type aliases",
);
}
if let Some((no_runtime_generics,)) = no_runtime_generics {
errors.error(
no_runtime_generics,
"no_runtime_generics is not implemented on PhantomConstGet type aliases",
);
}
if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
}
if let Some((custom_debug, _, _)) = custom_debug {
errors.error(custom_debug, "custom_debug is not valid on type aliases");
}
if let Some((custom_sim_display,)) = custom_sim_display {
errors.error(
custom_sim_display,
"custom_sim_display is not valid on type aliases",
);
}
if let Some((custom_bounds,)) = custom_bounds {
errors.error(
custom_bounds,
"custom_bounds is not implemented on PhantomConstGet type aliases",
);
}
let Some(generics) = PhantomConstAccessorGenerics::parse_opt(generics) else {
errors.error(ident, "#[hdl(get(...))] type alias must be of the form:\ntype MyTypeGetter<P: PhantomConstGet<MyType>> = RetType;");
errors.finish()?;
unreachable!();
};
errors.finish()?;
let ty_is_dyn_size = Self::ty_is_dyn_size(&ty);
Ok(Self::PhantomConstAccessor {
attrs,
options,
get,
vis,
type_token,
ident,
generics,
eq_token,
ty: *ty,
ty_is_dyn_size,
semi_token,
})
}
fn parse(item: ItemType) -> syn::Result<Self> {
let ItemType {
mut attrs,
@ -51,13 +297,42 @@ impl ParsedTypeAlias {
no_static,
no_runtime_generics: _,
cmp_eq,
ref mut get,
ref custom_debug,
custom_sim_display,
} = 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");
}
if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
}
if let Some((custom_debug, _, _)) = custom_debug {
errors.error(custom_debug, "custom_debug is not valid on type aliases");
}
if let Some((custom_sim_display,)) = custom_sim_display {
errors.error(
custom_sim_display,
"custom_sim_display is not valid on type aliases",
);
}
let generics = if custom_bounds.is_some() {
MaybeParsed::Unrecognized(generics)
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
@ -67,7 +342,7 @@ impl ParsedTypeAlias {
};
let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors);
errors.finish()?;
Ok(Self {
Ok(Self::TypeAlias {
attrs,
options,
vis,
@ -83,54 +358,159 @@ impl ParsedTypeAlias {
impl ToTokens for ParsedTypeAlias {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
options,
vis,
type_token,
ident,
generics,
eq_token,
ty,
semi_token,
} = self;
let ItemOptions {
outline_generated: _,
target,
custom_bounds: _,
no_static: _,
no_runtime_generics,
cmp_eq: _,
} = &options.body;
let target = get_target(target, ident);
let mut type_attrs = attrs.clone();
type_attrs.push(parse_quote_spanned! {ident.span()=>
#[allow(type_alias_bounds)]
});
ItemType {
attrs: type_attrs,
vis: vis.clone(),
type_token: *type_token,
ident: ident.clone(),
generics: generics.into(),
eq_token: *eq_token,
ty: Box::new(ty.clone().into()),
semi_token: *semi_token,
}
.to_tokens(tokens);
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(ty), None) =
(generics, ty, no_runtime_generics)
{
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
ty.make_hdl_type_expr(context)
})
match self {
Self::TypeAlias {
attrs,
options,
vis,
type_token,
ident,
generics,
eq_token,
ty,
semi_token,
} => {
let ItemOptions {
outline_generated: _,
target,
custom_bounds: _,
no_static: _,
no_runtime_generics,
cmp_eq: _,
get: _,
custom_debug: _,
custom_sim_display: _,
} = &options.body;
let target = get_target(target, ident);
let mut type_attrs = attrs.clone();
type_attrs.push(parse_quote_spanned! {ident.span()=>
#[allow(type_alias_bounds)]
});
ItemType {
attrs: type_attrs,
vis: vis.clone(),
type_token: *type_token,
ident: ident.clone(),
generics: generics.into(),
eq_token: *eq_token,
ty: Box::new(ty.clone().into()),
semi_token: *semi_token,
}
.to_tokens(tokens);
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(ty), None) =
(generics, ty, no_runtime_generics)
{
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
ty.make_hdl_type_expr(context)
})
}
}
Self::PhantomConstAccessor {
attrs,
options,
get: (_get_kw, _get_paren, get_expr),
vis,
type_token,
ident,
generics,
eq_token,
ty,
ty_is_dyn_size,
semi_token,
} => {
let ItemOptions {
outline_generated: _,
target: _,
custom_bounds: _,
no_static: _,
no_runtime_generics: _,
cmp_eq: _,
get: _,
custom_debug: _,
custom_sim_display: _,
} = &options.body;
let span = ident.span();
let mut type_attrs = attrs.clone();
type_attrs.push(parse_quote_spanned! {span=>
#[allow(type_alias_bounds)]
});
let type_param_ident = &generics.type_param.ident;
let syn_generics = Generics::from(generics);
ItemType {
attrs: type_attrs,
vis: vis.clone(),
type_token: *type_token,
ident: ident.clone(),
generics: syn_generics.clone(),
eq_token: *eq_token,
ty: parse_quote_spanned! {span=>
<#ty as ::fayalite::phantom_const::ReturnSelfUnchanged<#type_param_ident>>::Type
},
semi_token: *semi_token,
}
.to_tokens(tokens);
let generics_accumulation_ident =
format_ident!("__{}__GenericsAccumulation", ident);
ItemStruct {
attrs: vec![
common_derives(span, true),
parse_quote_spanned! {span=>
#[allow(non_camel_case_types)]
},
],
vis: vis.clone(),
struct_token: Token![struct](span),
ident: generics_accumulation_ident.clone(),
generics: Generics::default(),
fields: Fields::Unnamed(parse_quote_spanned! {span=>
(())
}),
semi_token: Some(Token![;](span)),
}
.to_tokens(tokens);
quote_spanned! {span=>
#[allow(non_upper_case_globals, dead_code)]
#vis const #ident: #generics_accumulation_ident = #generics_accumulation_ident(());
}
.to_tokens(tokens);
let mut wrapped_in_const = WrappedInConst::new(tokens, span);
let tokens = wrapped_in_const.inner();
let (impl_generics, _type_generics, where_clause) = syn_generics.split_for_impl();
let phantom_const_get_ty = &generics.type_param.phantom_const_get_bound.ty;
let index_output = if let Some(ty_is_dyn_size) = ty_is_dyn_size {
known_items::usize(ty_is_dyn_size.span).to_token_stream()
} else {
ty.to_token_stream()
};
quote_spanned! {span=>
#[allow(non_upper_case_globals)]
#[automatically_derived]
impl #impl_generics ::fayalite::__std::ops::Index<#type_param_ident>
for #generics_accumulation_ident
#where_clause
{
type Output = #index_output;
fn index(&self, __param: #type_param_ident) -> &Self::Output {
::fayalite::phantom_const::type_alias_phantom_const_get_helper::<#phantom_const_get_ty, #index_output>(
__param,
#get_expr,
)
}
}
}
.to_tokens(tokens);
}
}
}
}
pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result<TokenStream> {
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-");

View file

@ -7,10 +7,10 @@ use std::{collections::HashMap, fmt, mem};
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,
FieldsUnnamed, FnArg, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index,
ItemStruct, 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},
@ -18,6 +18,17 @@ use syn::{
token::{Brace, Bracket, Paren},
};
crate::options! {
#[options = CustomDebugOptions]
#[no_ident_fragment]
pub(crate) enum CustomDebugOption {
Type(type_),
Sim(sim),
MaskType(mask_type),
MaskSim(mask_sim),
}
}
crate::options! {
#[options = ItemOptions]
pub(crate) enum ItemOption {
@ -27,6 +38,9 @@ crate::options! {
NoStatic(no_static),
NoRuntimeGenerics(no_runtime_generics),
CmpEq(cmp_eq),
Get(get, Expr),
CustomDebug(custom_debug, CustomDebugOptions),
CustomSimDisplay(custom_sim_display),
}
}
@ -40,8 +54,36 @@ impl ItemOptions {
{
self.no_static = Some((kw::no_static(custom_bounds.span),));
}
if let Some((kw, _, custom_debug)) = &mut self.custom_debug {
if let CustomDebugOptions {
type_: None,
sim: None,
mask_type: None,
mask_sim: None,
} = custom_debug
{
*custom_debug = CustomDebugOptions {
type_: Some((kw::type_(kw.span),)),
sim: Some((kw::sim(kw.span),)),
mask_type: None,
mask_sim: None,
};
}
}
Ok(())
}
pub(crate) fn custom_debug(&self) -> &CustomDebugOptions {
self.custom_debug.as_ref().map(|v| &v.2).unwrap_or(
const {
&CustomDebugOptions {
type_: None,
sim: None,
mask_type: None,
mask_sim: None,
}
},
)
}
}
pub(crate) struct WrappedInConst<'a> {
@ -83,10 +125,17 @@ pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident:
}
}
pub(crate) fn common_derives(span: Span) -> Attribute {
pub(crate) fn common_derives(span: Span, include_debug: bool) -> Attribute {
let debug = include_debug
.then(|| {
quote_spanned! {span=>
::fayalite::__std::fmt::Debug
}
})
.into_iter();
parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
#(#debug,)*
::fayalite::__std::cmp::Eq,
::fayalite::__std::cmp::PartialEq,
::fayalite::__std::hash::Hash,
@ -2045,6 +2094,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 +2113,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 +2288,10 @@ macro_rules! impl_bounds {
$(
$Variant:ident,
)*
$(
#[has_body]
$VariantHasBody:ident($variant_has_body_ty:ty),
)*
$(
#[unknown]
$Unknown:ident,
@ -2079,6 +2301,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 +2311,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 +2355,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 +2381,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 +2394,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 +2412,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 +2423,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 +2438,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 +2458,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 +2479,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 +2503,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 +2560,8 @@ impl_bounds! {
Size,
StaticType,
Type,
#[has_body]
PhantomConstGet(PhantomConstGetBound),
#[unknown]
Unknown,
}
@ -2317,6 +2577,8 @@ impl_bounds! {
ResetType,
StaticType,
Type,
#[has_body]
PhantomConstGet(PhantomConstGetBound),
#[unknown]
Unknown,
}
@ -2332,6 +2594,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 +2610,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
ResetType,
StaticType,
Type,
PhantomConstGet,
Unknown,
} = value;
Self {
@ -2359,6 +2623,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
Size: None,
StaticType,
Type,
PhantomConstGet,
Unknown,
}
}
@ -2395,6 +2660,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 +2699,7 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
Size,
StaticType: None,
Type: None,
PhantomConstGet: None,
Unknown: vec![],
}
}
@ -2532,6 +2802,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),
}
}
@ -2750,7 +3023,7 @@ impl ParsedGenerics {
let span = ident.span();
ItemStruct {
attrs: vec![
common_derives(span),
common_derives(span, true),
parse_quote_spanned! {span=>
#[allow(non_camel_case_types)]
},
@ -3417,7 +3690,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 +4660,230 @@ impl MakeHdlTypeExpr for ParsedTypeTuple {
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum ParsedSimpleVisibility {
Public(Token![pub]),
PubCrate {
pub_token: Token![pub],
paren_token: Paren,
crate_token: Token![crate],
},
Inherited,
}
impl From<ParsedSimpleVisibility> for Visibility {
fn from(value: ParsedSimpleVisibility) -> Self {
match value {
ParsedSimpleVisibility::Public(v) => Visibility::Public(v),
ParsedSimpleVisibility::PubCrate {
pub_token,
paren_token,
crate_token,
} => Visibility::Restricted(syn::VisRestricted {
pub_token,
paren_token,
in_token: None,
path: Box::new(Ident::new("crate", crate_token.span).into()),
}),
ParsedSimpleVisibility::Inherited => Visibility::Inherited,
}
}
}
impl PartialOrd for ParsedSimpleVisibility {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ParsedSimpleVisibility {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.visibility_level().cmp(&other.visibility_level())
}
}
impl ParsedSimpleVisibility {
const VISIBILITY_LEVEL_INHERITED: u8 = 0;
const VISIBILITY_LEVEL_RESTRICTED: u8 = 1 + Self::VISIBILITY_LEVEL_INHERITED;
const VISIBILITY_LEVEL_PUB_CRATE: u8 = 1 + Self::VISIBILITY_LEVEL_RESTRICTED;
const VISIBILITY_LEVEL_PUB: u8 = 1 + Self::VISIBILITY_LEVEL_PUB_CRATE;
fn visibility_level(self) -> u8 {
match self {
Self::Public(_) => Self::VISIBILITY_LEVEL_PUB,
Self::PubCrate { .. } => Self::VISIBILITY_LEVEL_PUB_CRATE,
Self::Inherited => Self::VISIBILITY_LEVEL_INHERITED,
}
}
pub(crate) fn parse(vis: Visibility) -> Result<Self, syn::VisRestricted> {
match vis {
Visibility::Public(v) => Ok(Self::Public(v)),
Visibility::Restricted(syn::VisRestricted {
pub_token,
paren_token,
in_token: None,
path,
}) if path.is_ident("crate") => Ok(Self::PubCrate {
pub_token,
paren_token,
crate_token: Token![crate](path.get_ident().expect("just checked").span()),
}),
Visibility::Restricted(v) => Err(v),
Visibility::Inherited => Ok(Self::Inherited),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum ParsedVisibility {
Simple(ParsedSimpleVisibility),
Restricted(syn::VisRestricted),
}
impl From<ParsedVisibility> for Visibility {
fn from(value: ParsedVisibility) -> Self {
match value {
ParsedVisibility::Simple(v) => v.into(),
ParsedVisibility::Restricted(v) => Visibility::Restricted(v),
}
}
}
impl PartialOrd for ParsedVisibility {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(ParsedVisibility::Simple(l), ParsedVisibility::Simple(r)) => Some(l.cmp(r)),
(ParsedVisibility::Simple(l), ParsedVisibility::Restricted(_)) => Some(
l.visibility_level()
.cmp(&ParsedSimpleVisibility::VISIBILITY_LEVEL_RESTRICTED),
),
(ParsedVisibility::Restricted(_), ParsedVisibility::Simple(r)) => {
Some(ParsedSimpleVisibility::VISIBILITY_LEVEL_RESTRICTED.cmp(&r.visibility_level()))
}
(ParsedVisibility::Restricted(l), ParsedVisibility::Restricted(r)) => {
(l == r).then_some(std::cmp::Ordering::Equal)
}
}
}
}
impl ParsedVisibility {
#[allow(dead_code)]
pub(crate) fn parse(vis: Visibility) -> Self {
match ParsedSimpleVisibility::parse(vis) {
Ok(simple) => Self::Simple(simple),
Err(restricted) => Self::Restricted(restricted),
}
}
#[allow(dead_code)]
pub(crate) fn min<'a>(&'a self, other: &'a Self) -> Option<&'a Self> {
self.partial_cmp(other)
.map(|ord| if ord.is_lt() { self } else { other })
}
}
pub(crate) struct CustomDebugTrait<'a> {
pub(crate) trait_path: &'a Path,
pub(crate) fn_name: &'a Ident,
pub(crate) this_arg: &'a FnArg,
}
#[must_use]
pub(crate) fn create_struct_debug_impl(
item_struct: &ItemStruct,
debug_struct_name: &str,
custom_debug_trait: Option<CustomDebugTrait<'_>>,
) -> TokenStream {
let ident = &item_struct.ident;
let span = ident.span();
let (impl_generics, type_generics, where_clause) = item_struct.generics.split_for_impl();
let trait_path;
let fn_name;
let this_arg;
let CustomDebugTrait {
trait_path,
fn_name,
this_arg,
} = match custom_debug_trait {
Some(v) => v,
None => {
trait_path = parse_quote_spanned! {span=>
::fayalite::__std::fmt::Debug
};
fn_name = parse_quote_spanned! {span=>
fmt
};
this_arg = parse_quote_spanned! {span=>
&self
};
CustomDebugTrait {
trait_path: &trait_path,
fn_name: &fn_name,
this_arg: &this_arg,
}
}
};
let this_arg_name = match this_arg {
FnArg::Receiver(this_arg) => this_arg.self_token.to_token_stream(),
FnArg::Typed(this_arg) => match &*this_arg.pat {
syn::Pat::Ident(pat_ident) => pat_ident.ident.to_token_stream(),
_ => unreachable!(),
},
};
match &item_struct.fields {
Fields::Named(fields) => {
let field_idents = fields
.named
.iter()
.map(|v| v.ident.as_ref().expect("known to have field name"));
let field_names = field_idents.clone().map(|v| v.to_string());
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics #trait_path for #ident #type_generics
#where_clause
{
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
let _ = #this_arg_name;
f.debug_struct(#debug_struct_name)
#(.field(#field_names, &#this_arg_name.#field_idents))*
.finish()
}
}
}
}
Fields::Unnamed(fields) => {
let field_members = fields
.unnamed
.iter()
.enumerate()
.map(|(index, _)| syn::Index {
index: index as _,
span,
});
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics #trait_path for #ident #type_generics
#where_clause
{
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
let _ = #this_arg_name;
f.debug_tuple(#debug_struct_name)
#(.field(&#this_arg_name.#field_members))*
.finish()
}
}
}
}
Fields::Unit => quote_spanned! {ident.span()=>
#[automatically_derived]
impl #impl_generics #trait_path for #ident #type_generics
#where_clause
{
fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
let _ = #this_arg_name;
f.write_str(#debug_struct_name)
}
}
},
}
}

View file

@ -42,6 +42,7 @@ pub(crate) trait CustomToken:
mod kw {
pub(crate) use syn::token::Extern as extern_;
pub(crate) use syn::token::Type as type_;
macro_rules! custom_keyword {
($kw:ident) => {
@ -66,6 +67,7 @@ mod kw {
}
custom_keyword!(__evaluated_cfgs);
custom_keyword!(add_platform_io);
custom_keyword!(all);
custom_keyword!(any);
custom_keyword!(cfg);
@ -74,13 +76,18 @@ mod kw {
custom_keyword!(cmp_eq);
custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds);
custom_keyword!(custom_debug);
custom_keyword!(custom_sim_display);
custom_keyword!(flip);
custom_keyword!(get);
custom_keyword!(hdl);
custom_keyword!(hdl_module);
custom_keyword!(incomplete_wire);
custom_keyword!(input);
custom_keyword!(instance);
custom_keyword!(m);
custom_keyword!(mask_sim);
custom_keyword!(mask_type);
custom_keyword!(memory);
custom_keyword!(memory_array);
custom_keyword!(memory_with_init);
@ -885,7 +892,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 +910,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);
}

View 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_impl_generics, struct_type_generics, _struct_where_clause) =
struct_generics.split_for_impl();
let struct_ty = quote! {#fn_name #struct_type_generics};
let struct_ty = match struct_generics {
Some(struct_generics) => {
let (_struct_impl_generics, struct_type_generics, _struct_where_clause) =
struct_generics.split_for_impl();
quote! {#fn_name #struct_type_generics}
}
None => quote! {::fayalite::bundle::Bundle},
};
body_sig.ident = parse_quote! {__body};
body_sig
.inputs

View file

@ -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,
};

View file

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

View file

@ -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![:](field_name.span()))
.to_tokens(tokens);
@ -450,7 +460,7 @@ trait ParseMatchPat: Sized {
Pat::Ident(PatIdent {
attrs: _,
by_ref,
mutability,
mut mutability,
ident,
subpat,
}) => {
@ -459,10 +469,13 @@ trait ParseMatchPat: Sized {
.errors
.error(by_ref, "ref not allowed in #[hdl] patterns");
}
if let Some(mutability) = mutability {
state
.errors
.error(mutability, "mut not allowed in #[hdl] patterns");
if let Some(mut_token) = mutability {
if state.sim.is_none() {
state
.errors
.error(mut_token, "mut not allowed in #[hdl] patterns");
mutability = None; // avoid duplicate errors
}
}
if let Some((at_token, _)) = subpat {
state
@ -474,18 +487,26 @@ trait ParseMatchPat: Sized {
variant_path,
enum_path,
variant_name,
}) => Self::enum_variant(
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
field: None,
},
),
}) => {
if let Some(mut_token) = mutability {
state
.errors
.error(mut_token, "mut not allowed on unit variants");
}
Self::enum_variant(
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
field: None,
},
)
}
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
mutability,
ident,
}))),
}
@ -980,15 +1001,16 @@ struct HdlMatchParseState<'a> {
struct HdlLetPatVisitState<'a> {
errors: &'a mut Errors,
bindings: BTreeSet<&'a Ident>,
bindings: BTreeMap<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,25 @@ 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;
// use method syntax to deduce what type to convert to
let __match_value = ::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 +1132,7 @@ impl Visitor<'_> {
__match_variant,
);
#let_token #pat #eq_token __match_variant #semi_token
(#(#bindings,)* __scope,)
(#(#bindings_idents,)* __scope,)
};
}
};
@ -1142,8 +1169,11 @@ 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;
// use method syntax to deduce what type to convert to
let __match_value = ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value)
.__fayalite_match_sim_value();
#match_token __match_value {
#(#arms)*
}
}

View file

@ -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,10 +26,12 @@ hashbrown.workspace = true
jobslot.workspace = true
num-bigint.workspace = true
num-traits.workspace = true
os_pipe.workspace = true
once_cell.workspace = true
ordered-float.workspace = true
petgraph.workspace = true
serde_json.workspace = true
serde.workspace = true
sha2.workspace = true
tempfile.workspace = true
vec_map.workspace = true
which.workspace = true

View file

@ -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)))
});
}

View 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))
})?))
},
);
}

View file

@ -31,3 +31,114 @@
//! }
//! }
//! ```
//!
//! You can also use `#[hdl(sim)] let` to destructure [`SimValue`]s (or anything that implements [`ToSimValue`]).
//!
//! [`SimValue`]: crate::sim::value::SimValue
//! [`ToSimValue`]: crate::sim::value::ToSimValue
//!
//! ```
//! # use fayalite::prelude::*;
//! #[hdl]
//! struct MyStruct<T> {
//! a: UInt<8>,
//! b: Bool,
//! c: T,
//! }
//!
//! #[hdl]
//! fn destructure<T: Type>(v: SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! mut b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: SimValue<UInt<8>> = a;
//! let _: SimValue<Bool> = b;
//! let _: SimValue<T> = c;
//! *b = false; // can modify b since mut was used
//! }
//!
//! #[hdl]
//! fn destructure_ref<'a, T: Type>(v: &'a SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: &'a SimValue<UInt<8>> = a;
//! let _: &'a SimValue<Bool> = b;
//! let _: &'a SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_mut<'a, T: Type>(v: &'a mut SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! **b = true; // you can modify v by modifying b which borrows from it
//!
//! // that gives these types:
//! let _: &'a mut SimValue<UInt<8>> = a;
//! let _: &'a mut SimValue<Bool> = b;
//! let _: &'a mut SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_inner<T: Type>(v: <MyStruct<T> as Type>::SimValue) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! mut b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: SimValue<UInt<8>> = a;
//! let _: SimValue<Bool> = b;
//! let _: SimValue<T> = c;
//! *b = false; // can modify b since mut was used
//! }
//!
//! #[hdl]
//! fn destructure_inner_ref<'a, T: Type>(v: &'a <MyStruct<T> as Type>::SimValue) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: &'a SimValue<UInt<8>> = a;
//! let _: &'a SimValue<Bool> = b;
//! let _: &'a SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_inner_mut<'a, T: Type>(v: &'a mut <MyStruct<T> as Type>::SimValue) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! **b = true; // you can modify v by modifying b which borrows from it
//!
//! // that gives these types:
//! let _: &'a mut SimValue<UInt<8>> = a;
//! let _: &'a mut SimValue<Bool> = b;
//! let _: &'a mut SimValue<T> = c;
//! }
//! ```

View file

@ -9,3 +9,110 @@
//!
//! `#[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_inner_move<T: Type>(v: <MyEnum<T> as Type>::SimValue) -> String {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => String::from("got A"),
//! MyEnum::<T>::B(mut b) => {
//! let _: SimValue<Bool> = b; // b has this type
//! let text = format!("got B({b})");
//! *b = true; // can modify b since mut was used
//! text
//! }
//! _ => String::from("something else"),
//! }
//! }
//!
//! #[hdl]
//! fn match_inner_ref<'a, T: Type>(v: &'a <MyEnum<T> as Type>::SimValue) -> u32 {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => 1,
//! MyEnum::<T>::B(b) => {
//! let _: &'a SimValue<Bool> = b; // b has this type
//! println!("got B({b})");
//! 5
//! }
//! _ => 42,
//! }
//! }
//!
//! #[hdl]
//! fn match_inner_mut<'a, T: Type>(v: &'a mut <MyEnum<T> as Type>::SimValue) -> Option<&'a mut SimValue<T>> {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => None,
//! MyEnum::<T>::B(b) => {
//! println!("got B({b})");
//! **b = true; // you can modify v by modifying b which borrows from it
//! let _: &'a mut SimValue<Bool> = b; // b has this type
//! None
//! }
//! MyEnum::<T>::C(v) => Some(v), // you can return matched values
//! _ => None, // HDL enums can have invalid discriminants, so we need this extra match arm
//! }
//! }
//! ```

View file

@ -145,52 +145,73 @@ pub struct DocStringAnnotation {
macro_rules! make_annotation_enum {
(
#[$non_exhaustive:ident]
$(#[$meta:meta])*
$vis:vis enum $Annotation:ident {
$($Variant:ident($T:ident),)*
$vis:vis enum $AnnotationEnum:ident {
$($Variant:ident($T:ty),)*
}
) => {
crate::annotations::make_annotation_enum!(@require_non_exhaustive $non_exhaustive);
#[$non_exhaustive]
$(#[$meta])*
$vis enum $Annotation {
#[derive(Clone, PartialEq, Eq, Hash)]
$vis enum $AnnotationEnum {
$($Variant($T),)*
}
$(impl IntoAnnotations for $T {
type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[$Annotation::$Variant(self)]
impl std::fmt::Debug for $AnnotationEnum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(Self::$Variant(v) => v.fmt(f),)*
}
}
}
impl IntoAnnotations for &'_ $T {
type IntoAnnotations = [$Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[$Annotation::$Variant(*self)]
$(impl From<$T> for crate::annotations::Annotation {
fn from(v: $T) -> Self {
$AnnotationEnum::$Variant(v).into()
}
}
impl IntoAnnotations for &'_ mut $T {
type IntoAnnotations = [$Annotation; 1];
impl crate::annotations::IntoAnnotations for $T {
type IntoAnnotations = [crate::annotations::Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[$Annotation::$Variant(*self)]
[self.into()]
}
}
impl IntoAnnotations for Box<$T> {
type IntoAnnotations = [$Annotation; 1];
impl crate::annotations::IntoAnnotations for &'_ $T {
type IntoAnnotations = [crate::annotations::Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[$Annotation::$Variant(*self)]
[crate::annotations::Annotation::from(self.clone())]
}
}
impl crate::annotations::IntoAnnotations for &'_ mut $T {
type IntoAnnotations = [crate::annotations::Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[crate::annotations::Annotation::from(self.clone())]
}
}
impl crate::annotations::IntoAnnotations for Box<$T> {
type IntoAnnotations = [crate::annotations::Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[crate::annotations::Annotation::from(*self)]
}
})*
};
(@require_non_exhaustive non_exhaustive) => {};
}
pub(crate) use make_annotation_enum;
make_annotation_enum! {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum Annotation {
DontTouch(DontTouchAnnotation),
@ -199,6 +220,7 @@ make_annotation_enum! {
BlackBoxPath(BlackBoxPathAnnotation),
DocString(DocStringAnnotation),
CustomFirrtl(CustomFirrtlAnnotation),
Xilinx(crate::vendor::xilinx::XilinxAnnotation),
}
}

View file

@ -3,23 +3,23 @@
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,
OpaqueSimValueWritten, StaticType, Type, TypeProperties, TypeWithDeref,
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties, TypeWithDeref,
serde_impls::SerdeCanonicalType,
},
util::ConstUsize,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
use std::{iter::FusedIterator, ops::Index};
use std::{borrow::Cow, fmt, iter::FusedIterator, ops::Index};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
@ -28,8 +28,8 @@ pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
type_properties: TypeProperties,
}
impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<T: Type, Len: Size> fmt::Debug for ArrayType<T, Len> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Array<{:?}, {}>", self.element, self.len())
}
}
@ -109,14 +109,42 @@ impl<T: StaticType, Len: KnownSize> Default for ArrayType<T, Len> {
}
}
struct MakeType<T: StaticType>(Interned<T>);
impl<T: StaticType> From<MakeType<T>> for Interned<T> {
fn from(value: MakeType<T>) -> Self {
value.0
}
}
impl<T: StaticType> Default for MakeType<T> {
fn default() -> Self {
Self(T::TYPE.intern_sized())
}
}
struct MakeMaskType<T: StaticType>(Interned<T::MaskType>);
impl<T: StaticType> From<MakeMaskType<T>> for Interned<T::MaskType> {
fn from(value: MakeMaskType<T>) -> Self {
value.0
}
}
impl<T: StaticType> Default for MakeMaskType<T> {
fn default() -> Self {
Self(T::MASK_TYPE.intern_sized())
}
}
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
const TYPE: Self = Self {
element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
element: LazyInterned::new_const::<MakeType<T>>(),
len: Len::SIZE,
type_properties: Self::TYPE_PROPERTIES,
};
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
element: LazyInterned::new_const::<MakeMaskType<T>>(),
len: Len::SIZE,
type_properties: Self::MASK_TYPE_PROPERTIES,
};
@ -154,6 +182,15 @@ impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
}
}
impl<T: Type, Len: Size> SimValueDebug for ArrayType<T, Len> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
type BaseType = Array;
type MaskType = ArrayType<T::MaskType, Len>;
@ -221,7 +258,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 +275,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 +363,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 +394,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 +403,19 @@ where
.cast_to_bits()
.any_one_bits()
}
}
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_eq(
lhs: Valueless<Self>,
rhs: Valueless<ArrayType<Rhs, Len>>,
) -> Valueless<Bool> {
assert_eq!(lhs.ty().len(), rhs.ty().len());
Valueless::new(Bool)
}
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 +426,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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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)]
}

View 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()),
]
}

View 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)
}
}

View 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();
}

View 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),
]
}

View file

@ -3,22 +3,24 @@
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,
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type,
TypeProperties, TypeWithDeref, impl_match_variant_as_self,
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug,
StaticType, Type, TypeProperties, TypeWithDeref, impl_match_variant_as_self,
},
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 {
@ -269,9 +271,17 @@ impl Type for Bundle {
}
}
impl SimValueDebug for Bundle {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
pub trait BundleType: Type<BaseType = Bundle> {
type Builder: Default;
type FilledBuilder: ToExpr<Type = Self>;
fn fields(&self) -> Interned<[BundleField]>;
}
@ -318,7 +328,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 +364,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 +384,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 +421,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 +452,12 @@ macro_rules! impl_tuple_builder_fields {
($global:tt []) => {};
}
macro_rules! get_unit_ty {
($($tt:tt)*) => {
()
};
}
macro_rules! impl_tuples {
(
[$({
@ -474,6 +480,14 @@ macro_rules! impl_tuples {
#[var($var)]
})*]
}
impl<$($T: Type,)*> SimValueDebug for ($($T,)*) {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<$($T: Type,)*> Type for ($($T,)*) {
type BaseType = Bundle;
type MaskType = ($($T::MaskType,)*);
@ -545,11 +559,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 +585,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 +668,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 +690,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()
}
}
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_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)
}
#[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)*] []);
@ -717,6 +790,15 @@ impl_tuples! {
]
}
impl<T: ?Sized + Send + Sync + 'static> SimValueDebug for PhantomData<T> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
type BaseType = Bundle;
type MaskType = ();
@ -781,9 +863,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 +880,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 +898,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)

View file

@ -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)
}
}

View file

@ -1,17 +1,21 @@
// 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,
OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties,
impl_match_variant_as_self,
},
};
use bitvec::{bits, order::Lsb0};
use std::fmt;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct Clock;
@ -68,6 +72,15 @@ impl Type for Clock {
}
}
impl SimValueDebug for Clock {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl Clock {
pub fn type_properties(self) -> TypeProperties {
Self::TYPE_PROPERTIES
@ -91,29 +104,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 +143,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()
}
}

View file

@ -2,10 +2,7 @@
// See Notices.txt for copyright information
use crate::{
expr::{
Expr, ToExpr,
ops::{ExprPartialEq, VariantAccess},
},
expr::{Expr, ToExpr, ValueType, ops::VariantAccess},
hdl,
int::{Bool, UIntValue},
intern::{Intern, Interned},
@ -13,12 +10,12 @@ use crate::{
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect,
enum_match_variants_helper, incomplete_wire, wire,
},
sim::value::{SimValue, SimValuePartialEq},
sim::value::{SimValue, ToSimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{
CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize,
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type,
TypeProperties,
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug,
StaticType, Type, TypeProperties,
},
util::HashMap,
};
@ -413,6 +410,15 @@ impl Type for Enum {
}
}
impl SimValueDebug for Enum {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct EnumPaddingSimValue {
bits: Option<UIntValue>,
@ -599,7 +605,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 +717,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)
}
}
@ -726,85 +732,12 @@ pub fn enum_type_to_sim_builder<T: EnumType>(v: T) -> T::SimBuilder {
v.into()
}
#[hdl]
#[hdl(cmp_eq)]
pub enum HdlOption<T: Type> {
HdlNone,
HdlSome(T),
}
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
#[hdl]
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_eq = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
HdlNone => connect(cmp_eq, false),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_eq, false),
HdlNone => connect(cmp_eq, true),
}
}
}
cmp_eq
}
#[hdl]
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_ne = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
HdlNone => connect(cmp_ne, true),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_ne, true),
HdlNone => connect(cmp_ne, false),
}
}
}
cmp_ne
}
}
impl<Lhs: SimValuePartialEq<Rhs>, Rhs: Type> SimValuePartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<HdlOption<Rhs>>) -> bool {
type SimValueMatch<T> = <T as Type>::SimValue;
match (&**this, &**other) {
(SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_)) => {
true
}
(SimValueMatch::<Self>::HdlSome(..), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_))
| (SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlSome(..)) => {
false
}
(
SimValueMatch::<Self>::HdlSome(l, _),
SimValueMatch::<HdlOption<Rhs>>::HdlSome(r, _),
) => l == r,
}
}
}
#[allow(non_snake_case)]
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
HdlOption[T::TYPE].HdlNone()
@ -813,7 +746,124 @@ 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> From<SimValue<HdlOption<T>>> for Option<SimValue<T>> {
#[hdl]
fn from(value: SimValue<HdlOption<T>>) -> Self {
#[hdl(sim)]
match value {
HdlSome(v) => Some(v),
HdlNone => None,
}
}
}
impl<'a, T: Type> From<&'a SimValue<HdlOption<T>>> for Option<&'a SimValue<T>> {
#[hdl]
fn from(value: &'a SimValue<HdlOption<T>>) -> Self {
#[hdl(sim)]
match value {
HdlSome(v) => Some(v),
HdlNone => None,
}
}
}
impl<'a, T: Type> From<&'a mut SimValue<HdlOption<T>>> for Option<&'a mut SimValue<T>> {
#[hdl]
fn from(value: &'a mut SimValue<HdlOption<T>>) -> Self {
#[hdl(sim)]
match value {
HdlSome(v) => Some(v),
HdlNone => None,
}
}
}
impl<T: ValueType<Type: StaticType<MaskType: StaticType>>> ValueType for Option<T> {
type Type = HdlOption<T::Type>;
type ValueCategory = T::ValueCategory;
fn ty(&self) -> Self::Type {
StaticType::TYPE
}
}
impl<T: Type, V: ToSimValueWithType<T>> ToSimValueWithType<HdlOption<T>> for Option<V> {
#[hdl]
fn to_sim_value_with_type(&self, ty: HdlOption<T>) -> SimValue<HdlOption<T>> {
match self {
Some(v) =>
{
#[hdl(sim)]
ty.HdlSome(v)
}
None =>
{
#[hdl(sim)]
ty.HdlNone()
}
}
}
#[hdl]
fn into_sim_value_with_type(self, ty: HdlOption<T>) -> SimValue<HdlOption<T>> {
match self {
Some(v) =>
{
#[hdl(sim)]
ty.HdlSome(v)
}
None =>
{
#[hdl(sim)]
ty.HdlNone()
}
}
}
}
impl<T: ToSimValue<Type: StaticType<MaskType: StaticType>>> ToSimValue for Option<T> {
#[hdl]
fn to_sim_value(&self) -> SimValue<Self::Type> {
match self {
Some(v) =>
{
#[hdl(sim)]
HdlSome(v)
}
None =>
{
#[hdl(sim)]
HdlNone()
}
}
}
#[hdl]
fn into_sim_value(self) -> SimValue<Self::Type> {
match self {
Some(v) =>
{
#[hdl(sim)]
HdlSome(v)
}
None =>
{
#[hdl(sim)]
HdlNone()
}
}
}
}
impl<T: ToExpr<Type: StaticType<MaskType: StaticType>>> ToExpr for Option<T> {
fn to_expr(&self) -> Expr<Self::Type> {
match self {
Some(v) => HdlSome(v),
None => HdlNone(),
}
}
}
impl<T: Type> HdlOption<T> {
@ -853,7 +903,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 +911,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 +929,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 +944,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 +1019,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 +1044,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 +1059,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 +1071,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 +1083,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 +1095,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 +1107,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 +1125,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 +1143,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 +1157,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

View 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}
}

View file

@ -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()),)*

View 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,
);
}

View file

@ -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) {

File diff suppressed because it is too large Load diff

View file

@ -4,17 +4,17 @@
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,
StaticType, Type, TypeProperties, impl_match_variant_as_self,
SimValueDebug, StaticType, Type, TypeProperties, impl_match_variant_as_self,
},
};
use bitvec::{order::Lsb0, view::BitView};
@ -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"];
@ -94,9 +94,17 @@ impl Type for UIntInRangeMaskType {
}
}
impl SimValueDebug for UIntInRangeMaskType {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl BundleType for UIntInRangeMaskType {
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 +120,8 @@ impl BundleType for UIntInRangeMaskType {
flipped: false,
ty: range.canonical(),
},
][..]
.intern()
]
.intern_slice()
}
}
@ -136,30 +144,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 +335,25 @@ 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
}
}
impl<Start: Size, End: Size> SimValueDebug for $UIntInRangeType<Start, End> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
@ -393,7 +444,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 +459,8 @@ macro_rules! define_uint_in_range_type {
flipped: false,
ty: range.canonical(),
},
][..]
.intern()
]
.intern_slice()
}
}
@ -477,32 +527,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 +593,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 +654,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())
}
}

View file

@ -4,66 +4,191 @@
use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher};
use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec};
use hashbrown::HashTable;
use once_cell::race::OnceRef;
use serde::{Deserialize, Serialize};
use std::{
any::{Any, TypeId},
borrow::{Borrow, Cow},
cell::RefCell,
cmp::Ordering,
ffi::{OsStr, OsString},
fmt,
hash::{BuildHasher, Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
ops::Deref,
sync::{Mutex, RwLock},
path::{Path, PathBuf},
sync::RwLock,
};
mod interner;
mod type_map;
pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any {
fn get(&self) -> Interned<T>;
/// invariant: T must be zero-sized, `type_id` is unique for every possible T value.
struct LazyInternedLazyInner<T: ?Sized + 'static> {
type_id: TypeId,
value: T,
}
impl<T: ?Sized + Send + Sync + 'static, F: ?Sized + Fn() -> Interned<T> + Send + Sync + Any>
LazyInternedTrait<T> for F
{
fn get(&self) -> Interned<T> {
self()
impl<T: ?Sized + 'static> Hash for LazyInternedLazyInner<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
let Self { type_id, value: _ } = self;
type_id.hash(state);
}
}
#[repr(transparent)]
pub struct LazyInternedFn<T: ?Sized + Send + Sync + 'static>(pub &'static dyn LazyInternedTrait<T>);
impl<T: ?Sized + 'static> PartialEq for LazyInternedLazyInner<T> {
fn eq(&self, other: &Self) -> bool {
let Self { type_id, value: _ } = self;
*type_id == other.type_id
}
}
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedFn<T> {}
impl<T: ?Sized + 'static> Eq for LazyInternedLazyInner<T> {}
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedFn<T> {
impl<T: ?Sized + 'static> fmt::Debug for LazyInternedLazyInner<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LazyInternedLazyInner")
.finish_non_exhaustive()
}
}
impl<T: ?Sized + 'static> LazyInternedLazyInner<T> {
const fn new(value: T) -> Self
where
T: Sized,
{
const { assert!(size_of::<T>() == 0) };
Self {
type_id: TypeId::of::<T>(),
value,
}
}
}
pub struct LazyInternedLazy<T: ?Sized + Send + Sync + 'static>(
&'static LazyInternedLazyInner<dyn Fn() -> Interned<T> + Send + Sync>,
);
impl<T: ?Sized + Send + Sync + 'static> LazyInternedLazy<T> {
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self(&const { LazyInternedLazyInner::new(|| V::default().into()) })
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn interned(self) -> Interned<T> {
struct Map(hashbrown::HashTable<(TypeId, &'static (dyn Any + Send + Sync))>);
impl Map {
const EMPTY: Self = Self(hashbrown::HashTable::new());
fn get<T: ?Sized + Send + Sync + 'static>(
&self,
lazy_interned_lazy: LazyInternedLazy<T>,
hash: u64,
) -> Option<&'static Interned<T>> {
let &(_, v) = self.0.find(hash, |v| v.0 == lazy_interned_lazy.0.type_id)?;
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
unreachable!();
};
Some(retval)
}
fn get_or_insert<T: ?Sized + Send + Sync + 'static>(
&mut self,
lazy_interned_lazy: LazyInternedLazy<T>,
hash: u64,
v: &'static Interned<T>,
) -> &'static Interned<T> {
let entry = self
.0
.entry(
hash,
|&(k, _)| k == lazy_interned_lazy.0.type_id,
|&(k, _)| type_map::TypeIdBuildHasher.hash_one(k),
)
.or_insert_with(|| (lazy_interned_lazy.0.type_id, v));
let &(_, v) = entry.get();
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
unreachable!();
};
retval
}
}
static GLOBAL_CACHE: RwLock<Map> = RwLock::new(Map::EMPTY);
#[cold]
fn insert_in_thread_local_cache<T: ?Sized + Send + Sync + 'static>(
cache: &RefCell<Map>,
this: LazyInternedLazy<T>,
hash: u64,
) -> Interned<T> {
let read_lock = GLOBAL_CACHE.read().unwrap();
let v = read_lock.get(this, hash);
drop(read_lock);
let v = v.unwrap_or_else(|| {
let v = Box::leak(Box::new((this.0.value)()));
GLOBAL_CACHE.write().unwrap().get_or_insert(this, hash, v)
});
*cache.borrow_mut().get_or_insert(this, hash, v)
}
thread_local! {
static THREAD_LOCAL_CACHE: RefCell<Map> = const { RefCell::new(Map::EMPTY) };
}
let hash = type_map::TypeIdBuildHasher.hash_one(self.0.type_id);
THREAD_LOCAL_CACHE.with(|cache| {
let borrow = cache.borrow();
if let Some(v) = borrow.get(self, hash) {
*v
} else {
drop(borrow);
insert_in_thread_local_cache(cache, self, hash)
}
})
}
}
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedLazy<T> {}
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedLazy<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedFn<T> {
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedLazy<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.get_ptr_eq_with_type_id().hash(state);
self.0.hash(state);
}
}
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedFn<T> {}
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedLazy<T> {}
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedFn<T> {
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedLazy<T> {
fn eq(&self, other: &Self) -> bool {
self.0.get_ptr_eq_with_type_id() == other.0.get_ptr_eq_with_type_id()
self.0 == other.0
}
}
pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> {
Interned(Interned<T>),
Lazy(LazyInternedFn<T>),
Lazy(LazyInternedLazy<T>),
}
impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> {
pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
Self::Lazy(LazyInternedFn(v))
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self::Lazy(LazyInternedLazy::new_const::<V>())
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn interned(self) -> Interned<T> {
match self {
Self::Interned(retval) => retval,
Self::Lazy(retval) => retval.interned(),
}
}
}
@ -75,7 +200,7 @@ impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> {
impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {}
impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> {
impl<T: ?Sized + Sync + Send + 'static> Deref for LazyInterned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@ -83,9 +208,9 @@ impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> {
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Eq for LazyInterned<T> where Interned<T>: Eq {}
impl<T: ?Sized + Sync + Send + 'static> Eq for LazyInterned<T> where Interned<T>: Eq {}
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialEq for LazyInterned<T>
impl<T: ?Sized + Sync + Send + 'static> PartialEq for LazyInterned<T>
where
Interned<T>: PartialEq,
{
@ -94,7 +219,7 @@ where
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Ord for LazyInterned<T>
impl<T: ?Sized + Sync + Send + 'static> Ord for LazyInterned<T>
where
Interned<T>: Ord,
{
@ -103,7 +228,7 @@ where
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialOrd for LazyInterned<T>
impl<T: ?Sized + Sync + Send + 'static> PartialOrd for LazyInterned<T>
where
Interned<T>: PartialOrd,
{
@ -112,7 +237,7 @@ where
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Hash for LazyInterned<T>
impl<T: ?Sized + Sync + Send + 'static> Hash for LazyInterned<T>
where
Interned<T>: Hash,
{
@ -121,77 +246,6 @@ where
}
}
impl<T: ?Sized + Sync + Send + 'static> LazyInterned<T> {
pub fn interned(self) -> Interned<T>
where
T: Intern,
{
struct MemoizeInterned<T: ?Sized + Intern>(PhantomData<T>);
impl<T: ?Sized + Intern> Hash for MemoizeInterned<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<T: ?Sized + Intern> PartialEq for MemoizeInterned<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T: ?Sized + Intern> Eq for MemoizeInterned<T> {}
impl<T: ?Sized + Intern> Clone for MemoizeInterned<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Intern> Copy for MemoizeInterned<T> {}
impl<T: ?Sized + Intern> MemoizeGeneric for MemoizeInterned<T> {
type InputRef<'a> = LazyInternedFn<T>;
type InputOwned = LazyInternedFn<T>;
type InputCow<'a> = LazyInternedFn<T>;
type Output = Interned<T>;
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
a == b
}
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
*input
}
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
input
}
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
*input
}
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
input
}
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
input
}
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
input.0.get()
}
}
match self {
Self::Interned(retval) => retval,
Self::Lazy(retval) => MemoizeInterned(PhantomData).get(retval),
}
}
}
pub trait InternedCompare {
type InternedCompareKey: Ord + Hash;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey;
@ -287,15 +341,266 @@ impl InternedCompare for BitSlice {
}
}
impl InternedCompare for str {
type InternedCompareKey = PtrEqWithMetadata<Self>;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
PtrEqWithMetadata(this)
/// Safety: `as_bytes` and `from_bytes_unchecked` must return the same pointer as the input.
/// all values returned by `as_bytes` must be valid to pass to `from_bytes_unchecked`.
/// `into_bytes` must return the exact same thing as `as_bytes`.
/// `Interned<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(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,74 +621,27 @@ pub trait Intern: Any + Send + Sync {
}
}
struct InternerState<T: ?Sized + 'static + Send + Sync> {
table: HashTable<&'static T>,
hasher: DefaultBuildHasher,
}
pub struct Interner<T: ?Sized + 'static + Send + Sync> {
state: Mutex<InternerState<T>>,
}
impl<T: ?Sized + 'static + Send + Sync> Interner<T> {
fn get() -> &'static Interner<T> {
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
TYPE_ID_MAP.get_or_insert_default()
impl<T: ?Sized + Intern + ToOwned> From<Cow<'_, T>> for Interned<T> {
fn from(value: Cow<'_, T>) -> Self {
Intern::intern_cow(value)
}
}
impl<T: ?Sized + 'static + Send + Sync> Default for Interner<T> {
fn default() -> Self {
Self {
state: Mutex::new(InternerState {
table: HashTable::new(),
hasher: Default::default(),
}),
}
impl<T: ?Sized + Intern> From<&'_ T> for Interned<T> {
fn from(value: &'_ T) -> Self {
Intern::intern(value)
}
}
impl<T: ?Sized + 'static + Send + Sync + Hash + Eq + ToOwned> Interner<T> {
fn intern<F: FnOnce(Cow<'_, T>) -> &'static T>(
&self,
alloc: F,
value: Cow<'_, T>,
) -> Interned<T> {
let mut state = self.state.lock().unwrap();
let InternerState { table, hasher } = &mut *state;
let inner = *table
.entry(
hasher.hash_one(&*value),
|k| **k == *value,
|k| hasher.hash_one(&**k),
)
.or_insert_with(|| alloc(value))
.get();
Interned { inner }
impl<T: Intern + Clone> From<T> for Interned<T> {
fn from(value: T) -> Self {
Intern::intern_sized(value)
}
}
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<T> {
fn intern_sized(&self, value: Cow<'_, T>) -> Interned<T> {
self.intern(|value| Box::leak(Box::new(value.into_owned())), value)
}
}
impl<T: Clone + 'static + Send + Sync + Hash + Eq> Interner<[T]> {
fn intern_slice(&self, value: Cow<'_, [T]>) -> Interned<[T]> {
self.intern(|value| value.into_owned().leak(), value)
}
}
impl Interner<BitSlice> {
fn intern_bit_slice(&self, value: Cow<'_, BitSlice>) -> Interned<BitSlice> {
self.intern(|value| value.into_owned().leak(), value)
}
}
impl Interner<str> {
fn intern_str(&self, value: Cow<'_, str>) -> Interned<str> {
self.intern(|value| value.into_owned().leak(), value)
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))
}
}
@ -416,6 +674,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 +749,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 +812,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 +948,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))
@ -670,7 +964,7 @@ impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T {
where
Self: ToOwned,
{
Interner::get().intern_sized(this)
interner::Interner::get().intern_sized(this)
}
}
@ -690,7 +984,7 @@ impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for [T] {
where
Self: ToOwned,
{
Interner::get().intern_slice(this)
interner::Interner::get().intern_slice(this)
}
}
@ -710,27 +1004,7 @@ impl Intern for BitSlice {
where
Self: ToOwned,
{
Interner::get().intern_bit_slice(this)
}
}
impl Intern for str {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_owned(this: <Self as ToOwned>::Owned) -> Interned<Self>
where
Self: ToOwned,
{
Self::intern_cow(Cow::Owned(this))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self>
where
Self: ToOwned,
{
Interner::get().intern_str(this)
interner::Interner::get().intern_bit_slice(this)
}
}
@ -748,10 +1022,17 @@ pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy {
fn inner(self, input: Self::InputRef<'_>) -> Self::Output;
fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output {
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
thread_local! {
static TYPE_ID_MAP_CACHE: TypeIdMap = const { TypeIdMap::new() };
}
let map: &RwLock<(
DefaultBuildHasher,
HashTable<(Self, Self::InputOwned, Self::Output)>,
)> = TYPE_ID_MAP.get_or_insert_default();
)> = TYPE_ID_MAP_CACHE.with(|cache| {
cache.get_or_insert_with(|| {
TYPE_ID_MAP.get_or_insert_with(|| Box::leak(Default::default()))
})
});
fn hash_eq_key<'a, 'b, T: MemoizeGeneric>(
this: &'a T,
input: T::InputRef<'b>,
@ -853,3 +1134,35 @@ pub trait Memoize: 'static + Send + Sync + Hash + Eq + Copy {
self.get_cow(Cow::Borrowed(input))
}
}
/// like `once_cell::race::OnceBox` but for `Interned<T>` instead of `Box<T>`
pub struct OnceInterned<T: 'static + Send + Sync>(OnceRef<'static, T>);
impl<T: 'static + Send + Sync + fmt::Debug> fmt::Debug for OnceInterned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OnceInterned").field(&self.get()).finish()
}
}
impl<T: 'static + Send + Sync> Default for OnceInterned<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: 'static + Send + Sync> OnceInterned<T> {
pub const fn new() -> Self {
Self(OnceRef::new())
}
pub fn set(&self, v: Interned<T>) -> Result<(), ()> {
self.0.set(v.inner)
}
pub fn get(&self) -> Option<Interned<T>> {
self.0.get().map(|inner| Interned { inner })
}
pub fn get_or_init<F: FnOnce() -> Interned<T>>(&self, f: F) -> Interned<T> {
Interned {
inner: self.0.get_or_init(|| f().inner),
}
}
}

View file

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

View file

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

View file

@ -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;

View file

@ -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>) {

View file

@ -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();
@ -2290,13 +2396,16 @@ impl ModuleBuilder {
#[track_caller]
pub fn extern_module_simulation_fn<
Args: fmt::Debug + Clone + Hash + Eq + Send + Sync + 'static,
F: Copy + Fn(Args, crate::sim::ExternModuleSimulationState) -> Fut + Send + Sync + 'static,
Fut: IntoFuture<Output = ()> + 'static,
>(
&self,
args: Args,
f: fn(Args, crate::sim::ExternModuleSimulationState) -> Fut,
f: F,
) {
self.extern_module_simulation(crate::sim::SimGeneratorFn { args, f });
// use for compile-time side-effect
let _ = crate::sim::SimGeneratorFn::<Args, F, Fut>::ASSERT_F_IS_ZERO_SIZED;
self.extern_module_simulation(crate::sim::SimGeneratorFn::new(args, f));
}
}
@ -2498,7 +2607,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 +2659,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 +2774,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 +2817,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 +2853,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 +2927,6 @@ impl<T: Type> ModuleIO<T> {
unreachable!()
}
}
pub fn ty(&self) -> T {
self.ty
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]

View file

@ -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),
}
}

View file

@ -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,
}

View file

@ -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 =

View file

@ -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};

View file

@ -2,17 +2,14 @@
// 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},
intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize},
sim::value::{SimValue, ToSimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
StaticType, Type, TypeProperties, impl_match_variant_as_self,
SimValueDebug, StaticType, Type, TypeProperties, impl_match_variant_as_self,
serde_impls::{SerdeCanonicalType, SerdePhantomConst},
},
};
@ -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,16 +220,37 @@ 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 const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
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_const<V: Default + Into<Interned<T>>>() -> Self {
Self {
value: LazyInterned::new_lazy(v),
value: LazyInterned::new_const::<V>(),
}
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn get(self) -> Interned<T> {
self.value.interned()
}
@ -245,7 +264,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 +272,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()),
)
}
@ -308,6 +327,15 @@ impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<T> {
}
}
impl<T: ?Sized + PhantomConstValue> SimValueDebug for PhantomConst<T> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: ?Sized + PhantomConstValue> Default for PhantomConst<T>
where
Interned<T>: Default,
@ -321,9 +349,7 @@ impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T>
where
Interned<T>: Default,
{
const TYPE: Self = PhantomConst {
value: LazyInterned::new_lazy(&Interned::<T>::default),
};
const TYPE: Self = Self::new_const_default();
const MASK_TYPE: Self::MaskType = ();
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
@ -346,7 +372,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 +383,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 +495,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())
}

File diff suppressed because it is too large Load diff

View 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,
}

View file

@ -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,

View file

@ -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
}

View file

@ -1,16 +1,21 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// 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,
OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties,
impl_match_variant_as_self,
},
util::ConstUsize,
};
use bitvec::{bits, order::Lsb0};
use std::fmt;
mod sealed {
pub trait ResetTypeSealed {}
@ -19,15 +24,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>;
}
@ -98,6 +103,15 @@ macro_rules! reset_type {
}
}
impl SimValueDebug for $name {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl $name {
pub fn type_properties(self) -> TypeProperties {
Self::TYPE_PROPERTIES
@ -132,29 +146,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 +190,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

View file

@ -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 },
}
}
CanonicalType::PhantomConst(_) => {
let unit_layout = CompiledTypeLayout::get(());
CompiledTypeLayout {
ty: *input,
layout: unit_layout.layout,
body: unit_layout.body,
body: CompiledTypeLayoutBody::Array {
elements_non_empty: elements_non_empty.intern_deref(),
},
}
}
CanonicalType::PhantomConst(_) => CompiledTypeLayout {
ty: *input,
layout: TypeLayout::empty(),
body: CompiledTypeLayoutBody::PhantomConst,
},
CanonicalType::Bundle(bundle) => {
let mut layout = TypeLayout::empty();
let fields = bundle
@ -184,13 +239,9 @@ impl<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,18 +1778,23 @@ macro_rules! impl_compiler {
instantiated_module: InstantiatedModule,
target: MakeTraceDeclTarget,
source_location: SourceLocation,
empty_kind: impl FnOnce() -> SimTraceKind,
$($type_singular_field: impl FnOnce(StatePartIndex<$type_kind>) -> SimTraceKind,)*
) -> TraceLocation {
match target {
MakeTraceDeclTarget::Expr(target) => {
let compiled_value = self.compile_expr(instantiated_module, target);
let compiled_value = self.compiled_expr_to_value(compiled_value, source_location);
TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len().as_single() {
$(Some(TypeLenSingle::$type_singular_variant) => {
$type_singular_field(compiled_value.range.$type_plural_field.start)
})*
None => unreachable!(),
}))
if compiled_value.range.is_empty() {
TraceLocation::Scalar(self.new_sim_trace(empty_kind()))
} else {
TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len().as_single() {
$(Some(TypeLenSingle::$type_singular_variant) => {
$type_singular_field(compiled_value.range.$type_plural_field.start)
})*
None => unreachable!(),
}))
}
}
MakeTraceDeclTarget::Memory {
id,
@ -1723,9 +1825,10 @@ macro_rules! impl_compiler {
instantiated_module,
target,
source_location,
|| unreachable!(),
|index| SimTraceKind::SmallUInt { index, ty },
|index| SimTraceKind::BigUInt { index, ty },
|_| unreachable!(""),
|_| unreachable!(),
),
name,
ty,
@ -1737,9 +1840,10 @@ macro_rules! impl_compiler {
instantiated_module,
target,
source_location,
|| unreachable!(),
|index| SimTraceKind::SmallSInt { index, ty },
|index| SimTraceKind::BigSInt { index, ty },
|_| unreachable!(""),
|_| unreachable!(),
),
name,
ty,
@ -1751,9 +1855,10 @@ macro_rules! impl_compiler {
instantiated_module,
target,
source_location,
|| unreachable!(),
|index| SimTraceKind::SmallBool { index },
|index| SimTraceKind::BigBool { index },
|_| unreachable!(""),
|_| unreachable!(),
),
name,
flow,
@ -1798,15 +1903,16 @@ macro_rules! impl_compiler {
}
.into()
}
CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::Bundle(_) => unreachable!(),
CanonicalType::AsyncReset(_) => TraceAsyncReset {
location: self.make_trace_scalar_helper(
instantiated_module,
target,
source_location,
|| unreachable!(),
|index| SimTraceKind::SmallAsyncReset { index },
|index| SimTraceKind::BigAsyncReset { index },
|_| unreachable!(""),
|_| unreachable!(),
),
name,
flow,
@ -1817,9 +1923,10 @@ macro_rules! impl_compiler {
instantiated_module,
target,
source_location,
|| unreachable!(),
|index| SimTraceKind::SmallSyncReset { index },
|index| SimTraceKind::BigSyncReset { index },
|_| unreachable!(""),
|_| unreachable!(),
),
name,
flow,
@ -1831,21 +1938,38 @@ macro_rules! impl_compiler {
instantiated_module,
target,
source_location,
|| unreachable!(),
|index| SimTraceKind::SmallClock { index },
|index| SimTraceKind::BigClock { index },
|_| unreachable!(""),
|_| unreachable!(),
),
name,
flow,
}
.into(),
CanonicalType::PhantomConst(ty) => TracePhantomConst {
location: self.make_trace_scalar_helper(
instantiated_module,
target,
source_location,
|| SimTraceKind::PhantomConst { ty },
|_| unreachable!(),
|_| unreachable!(),
|_| unreachable!(),
),
name,
ty,
flow,
}
.into(),
CanonicalType::DynSimOnly(ty) => TraceSimOnly {
location: self.make_trace_scalar_helper(
instantiated_module,
target,
source_location,
|_| unreachable!(""),
|_| unreachable!(""),
|| unreachable!(),
|_| unreachable!(),
|_| unreachable!(),
|index| SimTraceKind::SimOnly { index, ty },
),
name,
@ -2110,6 +2234,7 @@ impl Compiler {
let id = TraceScalarId(self.traces.0.len());
self.traces.0.push(SimTrace {
kind,
maybe_changed: true,
state: (),
last_state: (),
});
@ -2295,16 +2420,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 +2772,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 +2784,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 +2950,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 +2964,7 @@ impl Compiler {
CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::DynSimOnly(_) => unreachable!(),
};
let dest_signed = match Expr::ty(expr) {
let dest_signed = match expr.ty() {
CanonicalType::UInt(_) => false,
CanonicalType::SInt(_) => true,
CanonicalType::Bool(_) => false,
@ -2859,22 +2978,23 @@ impl Compiler {
CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::DynSimOnly(_) => unreachable!(),
};
self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| {
match (src_signed, dest_signed) {
(false, false) | (true, true) => {
vec![Insn::Copy { dest, src }]
}
(false, true) => vec![Insn::CastToSInt {
dest,
src,
dest_width: 1,
}],
(true, false) => vec![Insn::CastToUInt {
dest,
src,
dest_width: 1,
}],
self.simple_nary_big_expr(instantiated_module, expr.ty(), [arg], |dest, [src]| match (
src_signed,
dest_signed,
) {
(false, false) | (true, true) => {
vec![Insn::Copy { dest, src }]
}
(false, true) => vec![Insn::CastToSInt {
dest,
src,
dest_width: 1,
}],
(true, false) => vec![Insn::CastToUInt {
dest,
src,
dest_width: 1,
}],
})
.into()
};
@ -2914,21 +3034,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 +3068,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 +3082,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 +3090,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 +3594,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 +3621,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 +3666,7 @@ impl Compiler {
vec![Insn::ReduceBitXor {
dest,
src,
input_width: Expr::ty(expr.arg()).width(),
input_width: expr.arg().ty().width(),
}]
},
)
@ -3574,7 +3680,7 @@ impl Compiler {
vec![Insn::ReduceBitXor {
dest,
src,
input_width: Expr::ty(expr.arg()).width(),
input_width: expr.arg().ty().width(),
}]
},
)
@ -3672,8 +3778,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 +3788,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 +3808,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 +4002,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 +4029,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 +4049,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 +4069,42 @@ 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)
});
// next value defaults to current value
self.compile_simple_connect(
[].intern_slice(),
value.into(),
value,
reg.source_location(),
);
self.compile_reg(
clk,
reset_and_init,
reg.source_location(),
instantiated_module,
value,
);
}
fn compile_declaration(
&mut self,
declaration: StmtDeclaration,
@ -4237,24 +4370,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 +4426,7 @@ impl Compiler {
start += element_bit_width;
}
}
CompiledTypeLayoutBody::PhantomConst => {}
CompiledTypeLayoutBody::Bundle { fields } => {
let CompiledTypeLayoutBody::Bundle {
fields: mask_fields,
@ -4860,6 +4994,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 +5104,7 @@ impl Compiler {
ModuleBody::Extern(ExternModuleBody {
verilog_name: _,
parameters: _,
clocks_for_past,
simulation,
}) => {
let Some(simulation) = simulation else {
@ -4904,10 +5121,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(),
});
}
}

View file

@ -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::{
@ -16,12 +17,11 @@ use bitvec::slice::BitSlice;
use num_bigint::BigInt;
use num_traits::{One, Signed, ToPrimitive, Zero};
use std::{
borrow::BorrowMut,
convert::Infallible,
fmt::{self, Write},
hash::Hash,
marker::PhantomData,
ops::{ControlFlow, Deref, DerefMut, Index, IndexMut},
ops::{ControlFlow, Deref, Index, IndexMut},
};
use vec_map::VecMap;
@ -196,13 +196,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 +253,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 +329,7 @@ impl Insn {
}
writeln!(f, ",")?;
}
write!(f.writer, "}}")
write!(f.into_inner(), "}}")
}
}
@ -905,6 +914,15 @@ impl<K: StatePartKind> StatePart<K> {
value: K::borrow_state(&mut self.value),
}
}
pub(crate) fn state_index_fetch_maybe_modified_flag(
&self,
part_index: StatePartIndex<K>,
) -> bool {
K::state_index_fetch_maybe_modified_flag(&self.value, part_index)
}
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
K::clear_all_maybe_modified_flags(&mut self.value)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
@ -912,56 +930,38 @@ pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> {
pub(crate) value: K::BorrowedState<'a>,
}
impl<
'a,
K: StatePartKind<
BorrowedState<'a>: DerefMut<Target: IndexMut<usize, Output = T> + BorrowMut<[T]>>,
>,
T,
> BorrowedStatePart<'a, K>
{
impl<K: StatePartKind> BorrowedStatePart<'_, K> {
pub(crate) fn get_disjoint_mut<const N: usize>(
&mut self,
indexes: [StatePartIndex<K>; N],
) -> [&mut T; N] {
(*self.value)
.borrow_mut()
.get_disjoint_mut(indexes.map(|v| v.value as usize))
.expect("indexes are disjoint")
) -> [&mut K::StateElement; N] {
K::borrowed_state_get_disjoint_mut(&mut self.value, indexes)
}
}
impl<K: StatePartKind<State: Deref<Target: Index<usize, Output = T>>>, T> Index<StatePartIndex<K>>
for StatePart<K>
{
type Output = T;
impl<K: StatePartKind> Index<StatePartIndex<K>> for StatePart<K> {
type Output = K::StateElement;
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
&self.value[index.value as usize]
K::state_index(&self.value, index)
}
}
impl<K: StatePartKind<State: DerefMut<Target: IndexMut<usize, Output = T>>>, T>
IndexMut<StatePartIndex<K>> for StatePart<K>
{
impl<K: StatePartKind> IndexMut<StatePartIndex<K>> for StatePart<K> {
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
&mut self.value[index.value as usize]
K::state_index_mut(&mut self.value, index)
}
}
impl<'a, K: StatePartKind<BorrowedState<'a>: Deref<Target: Index<usize, Output = T>>>, T>
Index<StatePartIndex<K>> for BorrowedStatePart<'a, K>
{
type Output = T;
impl<K: StatePartKind> Index<StatePartIndex<K>> for BorrowedStatePart<'_, K> {
type Output = K::StateElement;
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
&self.value[index.value as usize]
K::borrowed_state_index(&self.value, index)
}
}
impl<'a, K: StatePartKind<BorrowedState<'a>: DerefMut<Target: IndexMut<usize, Output = T>>>, T>
IndexMut<StatePartIndex<K>> for BorrowedStatePart<'a, K>
{
impl<K: StatePartKind> IndexMut<StatePartIndex<K>> for BorrowedStatePart<'_, K> {
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
&mut self.value[index.value as usize]
K::borrowed_state_index_mut(&mut self.value, index)
}
}
@ -1018,6 +1018,10 @@ macro_rules! make_state {
$($type_plural_field: self.$type_plural_field.borrow(),)*
}
}
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
$(self.$state_plural_field.clear_all_maybe_modified_flags();)*
$(self.$type_plural_field.clear_all_maybe_modified_flags();)*
}
}
#[derive(Debug)]

View file

@ -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},
@ -236,6 +236,7 @@ pub(crate) trait StatePartKind:
type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy;
type State: fmt::Debug + 'static + Clone;
type BorrowedState<'a>: 'a;
type StateElement;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State;
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>;
fn part_debug_data<BK: InsnsBuildingKind>(
@ -247,6 +248,31 @@ pub(crate) trait StatePartKind:
index: StatePartIndex<Self>,
f: &mut impl fmt::Write,
) -> fmt::Result;
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement;
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement;
fn state_index_fetch_maybe_modified_flag(
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool;
fn clear_all_maybe_modified_flags(state: &mut Self::State);
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement;
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement;
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N];
}
macro_rules! make_state_part_kinds {
@ -272,6 +298,7 @@ impl StatePartKind for StatePartKindMemories {
type LayoutData = MemoryData<Interned<BitSlice>>;
type State = Box<[MemoryData<BitBox>]>;
type BorrowedState<'a> = &'a mut [MemoryData<BitBox>];
type StateElement = MemoryData<BitBox>;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
layout_data
.iter()
@ -297,19 +324,89 @@ impl StatePartKind for StatePartKindMemories {
) -> fmt::Result {
write!(f, "{:#?}", &state.memories[index])
}
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state[part_index.as_usize()]
}
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
&mut state[part_index.as_usize()]
}
fn state_index_fetch_maybe_modified_flag(
_state: &Self::State,
_part_index: StatePartIndex<Self>,
) -> bool {
true
}
fn clear_all_maybe_modified_flags(_state: &mut Self::State) {}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state[part_index.as_usize()]
}
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
&mut state[part_index.as_usize()]
}
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N] {
state
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
.expect("indexes are disjoint")
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
pub(crate) struct StateAndModified<T, M> {
pub(crate) state: T,
pub(crate) modified: M,
}
impl<T: Deref<Target = [E]>, M: Deref<Target = [bool]>, E: fmt::Debug> fmt::Debug
for StateAndModified<T, M>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(self.state.iter().zip(self.modified.iter().copied()).map(
|(state, modified)| {
fmt::from_fn(move |f| {
state.fmt(f)?;
if modified {
f.write_str(" (modified)")?;
}
Ok(())
})
},
))
.finish()
}
}
impl StatePartKind for StatePartKindSmallSlots {
const NAME: &'static str = "SmallSlots";
type DebugData = SlotDebugData;
type LayoutData = ();
type State = Box<[SmallUInt]>;
type BorrowedState<'a> = &'a mut [SmallUInt];
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>;
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>;
type StateElement = SmallUInt;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
vec![0; layout_data.len()].into_boxed_slice()
StateAndModified {
state: vec![0; layout_data.len()].into_boxed_slice(),
modified: vec![false; layout_data.len()].into_boxed_slice(),
}
}
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
state
let StateAndModified { state, modified } = state;
StateAndModified { state, modified }
}
fn part_debug_data<BK: InsnsBuildingKind>(
state_layout: &StateLayout<BK>,
@ -330,19 +427,72 @@ impl StatePartKind for StatePartKindSmallSlots {
write!(f, "{value:#x} {}", value as SmallSInt)?;
Ok(())
}
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn state_index_fetch_maybe_modified_flag(
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool {
state.modified[part_index.as_usize()]
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N] {
for part_index in part_indexes {
state.modified[part_index.as_usize()] = true;
}
state
.state
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
.expect("indexes are disjoint")
}
}
impl StatePartKind for StatePartKindBigSlots {
const NAME: &'static str = "BigSlots";
type DebugData = SlotDebugData;
type LayoutData = ();
type State = Box<[BigInt]>;
type BorrowedState<'a> = &'a mut [BigInt];
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>;
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>;
type StateElement = BigInt;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
layout_data.iter().map(|_| BigInt::default()).collect()
let state: Box<[_]> = layout_data.iter().map(|_| BigInt::default()).collect();
StateAndModified {
modified: vec![false; state.len()].into_boxed_slice(),
state,
}
}
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
state
let StateAndModified { state, modified } = state;
StateAndModified { state, modified }
}
fn part_debug_data<BK: InsnsBuildingKind>(
state_layout: &StateLayout<BK>,
@ -361,19 +511,72 @@ impl StatePartKind for StatePartKindBigSlots {
) -> fmt::Result {
write!(f, "{:#x}", state.big_slots[index])
}
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn state_index_fetch_maybe_modified_flag(
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool {
state.modified[part_index.as_usize()]
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N] {
for part_index in part_indexes {
state.modified[part_index.as_usize()] = true;
}
state
.state
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
.expect("indexes are disjoint")
}
}
impl StatePartKind for StatePartKindSimOnlySlots {
const NAME: &'static str = "SimOnlySlots";
type DebugData = SlotDebugData;
type LayoutData = DynSimOnly;
type State = Box<[DynSimOnlyValue]>;
type BorrowedState<'a> = &'a mut [DynSimOnlyValue];
type State = StateAndModified<Box<[Self::StateElement]>, Box<[bool]>>;
type BorrowedState<'a> = StateAndModified<&'a mut [Self::StateElement], &'a mut [bool]>;
type StateElement = DynSimOnlyValue;
fn new_state(layout_data: &[Self::LayoutData]) -> Self::State {
layout_data.iter().map(|ty| ty.default_value()).collect()
let state: Box<[_]> = layout_data.iter().map(|ty| ty.default_value()).collect();
StateAndModified {
modified: vec![false; state.len()].into_boxed_slice(),
state,
}
}
fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> {
state
let StateAndModified { state, modified } = state;
StateAndModified { state, modified }
}
fn part_debug_data<BK: InsnsBuildingKind>(
state_layout: &StateLayout<BK>,
@ -392,6 +595,53 @@ impl StatePartKind for StatePartKindSimOnlySlots {
) -> fmt::Result {
write!(f, "{:?}", state.sim_only_slots[index])
}
fn state_index<'a>(
state: &'a Self::State,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn state_index_mut<'a>(
state: &'a mut Self::State,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn state_index_fetch_maybe_modified_flag(
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool {
state.modified[part_index.as_usize()]
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a Self::StateElement {
&state.state[part_index.as_usize()]
}
fn borrowed_state_index_mut<'a, 'b>(
state: &'a mut Self::BorrowedState<'b>,
part_index: StatePartIndex<Self>,
) -> &'a mut Self::StateElement {
state.modified[part_index.as_usize()] = true;
&mut state.state[part_index.as_usize()]
}
fn borrowed_state_get_disjoint_mut<'a, 'b, const N: usize>(
state: &'a mut Self::BorrowedState<'b>,
part_indexes: [StatePartIndex<Self>; N],
) -> [&'a mut Self::StateElement; N] {
for part_index in part_indexes {
state.modified[part_index.as_usize()] = true;
}
state
.state
.get_disjoint_mut(part_indexes.map(StatePartIndex::as_usize))
.expect("indexes are disjoint")
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -435,12 +685,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 +698,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(())
}

View file

@ -6,13 +6,17 @@ 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,
ty::{
CanonicalType, OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSlice,
OpaqueSimValueWriter, StaticType, Type, TypeProperties, impl_match_variant_as_self,
OpaqueSimValueWriter, SimValueDebug, StaticType, Type, TypeProperties,
impl_match_variant_as_self,
},
util::{
ConstUsize, HashMap,
@ -23,10 +27,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 +141,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 +162,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 +232,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 +270,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 +291,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 +313,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 +337,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(),
)
}
@ -342,17 +359,11 @@ impl<T: Type> DerefMut for SimValue<T> {
impl<T: Type> fmt::Debug for SimValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner = self.inner.share();
f.debug_struct("SimValue")
.field("ty", &inner.ty)
.field("value", &inner.value)
.finish()
T::SimValue::fmt(&**self, f)
}
}
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 +371,303 @@ 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
}
}
#[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<Width: Size> SimValuePartialEq<Self> for SIntType<Width> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> 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
}
}
}
impl SimValuePartialEq<Bool> for Bool {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Bool>) -> bool {
**this == **other
}
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 {}
};
}
pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> {
type Type: Type;
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, ty::Type};
use std::ops::{Deref, DerefMut};
macro_rules! wrapper {
(
$(pub struct $wrapper:ident<$T:ident>($inner:ty);)*
) => {
$(#[doc(hidden)]
pub struct $wrapper<$T>($inner);
impl<$T> $wrapper<$T> {
#[inline(always)]
pub fn new(value: $T) -> Self {
Self(<$inner>::new(value))
}
}
impl<$T> Deref for $wrapper<$T> {
type Target = $inner;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<$T> DerefMut for $wrapper<$T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
})*
};
}
wrapper! {
pub struct MatchSimValueHelperCheckSimValue<T>(MatchSimValueHelperCheckMutSimValue<T>);
pub struct MatchSimValueHelperCheckMutSimValue<T>(MatchSimValueHelperCheckRefSimValue<T>);
pub struct MatchSimValueHelperCheckRefSimValue<T>(MatchSimValueHelperCheckRefRefSimValue<T>);
pub struct MatchSimValueHelperCheckRefRefSimValue<T>(MatchSimValueHelperCheckRefMutSimValue<T>);
pub struct MatchSimValueHelperCheckRefMutSimValue<T>(MatchSimValueHelperCheckMutRefSimValue<T>);
pub struct MatchSimValueHelperCheckMutRefSimValue<T>(MatchSimValueHelperCheckMutMutSimValue<T>);
pub struct MatchSimValueHelperCheckMutMutSimValue<T>(MatchSimValueHelperIdentity<T>);
}
impl<T: Type> MatchSimValueHelperCheckSimValue<SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> T::SimValue {
SimValue::into_value(self.take())
}
}
impl<'a, T: Type> MatchSimValueHelperCheckMutSimValue<&'a mut SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'a mut T::SimValue {
self.take()
}
}
impl<'a, T: Type> MatchSimValueHelperCheckRefSimValue<&'a SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'a T::SimValue {
self.take()
}
}
impl<'a, 'b, T: Type> MatchSimValueHelperCheckRefRefSimValue<&'a &'b SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'b T::SimValue {
self.take()
}
}
impl<'a, 'b, T: Type> MatchSimValueHelperCheckRefMutSimValue<&'a &'b mut SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'a T::SimValue {
self.take()
}
}
impl<'a, 'b, T: Type> MatchSimValueHelperCheckMutRefSimValue<&'a mut &'b SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'b T::SimValue {
self.take()
}
}
impl<'a, 'b, T: Type> MatchSimValueHelperCheckMutMutSimValue<&'a mut &'b mut SimValue<T>> {
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> &'a mut T::SimValue {
self.take()
}
}
#[doc(hidden)]
pub struct MatchSimValueHelperIdentity<T>(Option<T>);
impl<T> MatchSimValueHelperIdentity<T> {
fn new(v: T) -> Self {
Self(Some(v))
}
#[inline(always)]
fn take(&mut self) -> T {
self.0.take().expect("known to be Some")
}
#[inline(always)]
pub fn __fayalite_match_sim_value(&mut self) -> T {
self.take()
}
}
#[doc(hidden)]
pub type MatchSimValueHelper<T> = MatchSimValueHelperCheckSimValue<T>;
}
pub trait ToSimValue: ToSimValueWithType<<Self as ValueType>::Type> + ValueType {
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type>;
#[track_caller]
@ -438,31 +709,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 +741,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 +796,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 +809,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 +821,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 +841,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 +855,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 +897,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 +932,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 +985,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 +1025,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 +1054,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 +1077,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 +1114,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 +1126,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 +1142,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 +1161,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())
}
@ -1143,6 +1401,15 @@ impl Type for DynSimOnly {
}
}
impl SimValueDebug for DynSimOnly {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: SimOnlyValueTrait> Type for SimOnly<T> {
type BaseType = DynSimOnly;
type MaskType = Bool;
@ -1208,6 +1475,15 @@ impl<T: SimOnlyValueTrait> Type for SimOnly<T> {
}
}
impl<T: SimOnlyValueTrait> SimValueDebug for SimOnly<T> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
impl<T: SimOnlyValueTrait> StaticType for SimOnly<T> {
const TYPE: Self = Self::new();
@ -1279,8 +1555,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 +1564,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<_>>");
}
}

View file

@ -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()
}

View file

@ -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,
},
@ -19,14 +21,31 @@ use crate::{
};
use bitvec::{order::Lsb0, slice::BitSlice};
use hashbrown::hash_map::Entry;
use sha2::{Digest, Sha256};
use std::{
collections::BTreeMap,
fmt::{self, Write as _},
io, mem,
num::NonZeroU64,
};
#[derive(Default)]
#[derive(Default, Clone)]
struct PathHash(Sha256);
impl PathHash {
fn joined(mut self, segment: impl AsRef<[u8]>) -> Self {
let segment = segment.as_ref();
self.0.update(u32::to_le_bytes(
segment.len().try_into().expect("path segment is too big"),
));
self.0.update(segment);
self
}
}
struct Scope {
last_inserted: HashMap<Interned<str>, usize>,
path_hash: PathHash,
}
#[derive(Copy, Clone)]
@ -59,6 +78,13 @@ impl fmt::Display for VerilogIdentifier {
}
impl Scope {
fn new(path_hash: PathHash) -> Self {
Self {
last_inserted: Default::default(),
path_hash,
}
}
fn new_identifier(&mut self, unescaped_name: Interned<str>) -> VerilogIdentifier {
let next_disambiguator = match self.last_inserted.entry(unescaped_name) {
Entry::Vacant(entry) => {
@ -161,6 +187,26 @@ impl<W: io::Write> fmt::Debug for VcdWriterDecls<W> {
}
}
/// pass in scope to ensure it's not available in child scope
fn try_write_vcd_scope<W: io::Write, R>(
writer: &mut W,
scope_type: &str,
scope_name: Interned<str>,
scope: Option<&mut Scope>,
f: impl FnOnce(&mut W, Option<&mut Scope>) -> io::Result<R>,
) -> io::Result<R> {
let Some(scope) = scope else {
return f(writer, None);
};
write_vcd_scope(
writer,
scope_type,
scope_name,
scope,
move |writer, scope| f(writer, Some(scope)),
)
}
/// pass in scope to ensure it's not available in child scope
fn write_vcd_scope<W: io::Write, R>(
writer: &mut W,
@ -169,12 +215,10 @@ fn write_vcd_scope<W: io::Write, R>(
scope: &mut Scope,
f: impl FnOnce(&mut W, &mut Scope) -> io::Result<R>,
) -> io::Result<R> {
writeln!(
writer,
"$scope {scope_type} {} $end",
scope.new_identifier(scope_name),
)?;
let retval = f(writer, &mut Scope::default())?;
let path_hash = scope.path_hash.clone().joined(scope_name);
let scope_name = scope.new_identifier(scope_name);
writeln!(writer, "$scope {scope_type} {scope_name} $end")?;
let retval = f(writer, &mut Scope::new(path_hash))?;
writeln!(writer, "$upscope $end")?;
Ok(retval)
}
@ -214,6 +258,7 @@ trait_arg! {
struct ArgModule<'a> {
properties: &'a mut VcdWriterProperties,
scope: &'a mut Scope,
instance_name: Option<Interned<str>>,
}
impl<'a> ArgModule<'a> {
@ -221,6 +266,7 @@ impl<'a> ArgModule<'a> {
ArgModule {
properties: self.properties,
scope: self.scope,
instance_name: self.instance_name,
}
}
}
@ -244,7 +290,7 @@ struct ArgInType<'a> {
sink_var_type: &'static str,
duplex_var_type: &'static str,
properties: &'a mut VcdWriterProperties,
scope: &'a mut Scope,
scope: Option<&'a mut Scope>,
}
impl<'a> ArgInType<'a> {
@ -254,7 +300,7 @@ impl<'a> ArgInType<'a> {
sink_var_type: self.sink_var_type,
duplex_var_type: self.duplex_var_type,
properties: self.properties,
scope: self.scope,
scope: self.scope.as_deref_mut(),
}
}
}
@ -283,24 +329,84 @@ 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),
}
}
}
fn write_vcd_id<W: io::Write>(writer: &mut W, mut id: usize) -> io::Result<()> {
let min_char = b'!';
let max_char = b'~';
let base = (max_char - min_char + 1) as usize;
loop {
let digit = (id % base) as u8 + min_char;
id /= base;
writer.write_all(&[digit])?;
if id == 0 {
break;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[repr(transparent)]
struct VcdId(NonZeroU64);
impl VcdId {
const CHAR_RANGE: std::ops::RangeInclusive<u8> = b'!'..=b'~';
const BASE: u8 = *Self::CHAR_RANGE.end() - *Self::CHAR_RANGE.start() + 1;
const LOW_HALF_CHARS: u32 = 5;
const LOW_HALF_MODULUS: u64 = (Self::BASE as u64).pow(Self::LOW_HALF_CHARS);
const fn from_str(s: &str) -> Option<Self> {
if s.is_empty() {
return None;
}
let mut retval = 0u64;
let mut bytes = s.as_bytes();
while let [ref rest @ .., digit] = *bytes {
bytes = rest;
let Some(digit) = digit.checked_sub(*Self::CHAR_RANGE.start()) else {
return None;
};
if digit >= Self::BASE {
return None;
}
let Some(v) = retval.checked_mul(Self::BASE as _) else {
return None;
};
let Some(v) = v.checked_add(digit as _) else {
return None;
};
retval = v;
}
let Some(retval) = NonZeroU64::new(retval) else {
return None;
};
Some(Self(retval))
}
Ok(())
#[must_use]
const fn write(self, out: &mut [u8]) -> usize {
let mut id = self.0.get();
let mut len = 0;
loop {
let digit = (id % Self::BASE as u64) as u8 + *Self::CHAR_RANGE.start();
id /= Self::BASE as u64;
if len < out.len() {
out[len] = digit;
}
len += 1;
if id == 0 {
break;
}
}
len
}
const MAX_ID_LEN: usize = Self(NonZeroU64::MAX).write(&mut []);
}
/// check that VcdId properly round-trips
const _: () = {
let s = "RoundTrip";
let Some(id) = VcdId::from_str(s) else {
unreachable!();
};
let mut buf = [0u8; VcdId::MAX_ID_LEN];
let len = id.write(&mut buf);
assert!(crate::util::const_bytes_cmp(buf.split_at(len).0, s.as_bytes()).is_eq());
};
fn write_vcd_id<W: io::Write>(writer: &mut W, id: VcdId) -> io::Result<()> {
let mut buf = [0u8; VcdId::MAX_ID_LEN];
let len = id.write(&mut buf);
writer.write_all(&buf[..len])
}
struct Escaped<T: fmt::Display>(T);
@ -343,12 +449,13 @@ impl<T: fmt::Display> fmt::Display for Escaped<T> {
fn write_vcd_var<W: io::Write>(
properties: &mut VcdWriterProperties,
scope: Option<&mut Scope>,
memory_element_part_body: MemoryElementPartBody,
writer: &mut W,
var_type: &str,
size: usize,
location: TraceLocation,
name: VerilogIdentifier,
name: Interned<str>,
) -> io::Result<()> {
let id = match location {
TraceLocation::Scalar(id) => id.as_usize(),
@ -381,9 +488,21 @@ fn write_vcd_var<W: io::Write>(
first_id + *element_index
}
};
write!(writer, "$var {var_type} {size} ")?;
write_vcd_id(writer, id)?;
writeln!(writer, " {name} $end")
if let Some(scope) = scope {
let path_hash = scope.path_hash.clone().joined(name);
let name = scope.new_identifier(name);
let id = properties
.scalar_id_to_vcd_id_map
.builder_get_or_insert(id, &path_hash);
write!(writer, "$var {var_type} {size} ")?;
write_vcd_id(writer, id)?;
writeln!(writer, " {name} $end")
} else {
properties
.scalar_id_to_vcd_id_map
.builder_unused_scalar_id(id);
Ok(())
}
}
impl WriteTrace for TraceUInt {
@ -411,12 +530,13 @@ impl WriteTrace for TraceUInt {
}
write_vcd_var(
properties,
scope,
MemoryElementPartBody::Scalar,
writer,
var_type,
ty.width(),
location,
scope.new_identifier(name),
name,
)
}
}
@ -491,12 +611,13 @@ impl WriteTrace for TraceEnumDiscriminant {
} = self;
write_vcd_var(
properties,
scope,
MemoryElementPartBody::EnumDiscriminant { ty },
writer,
"string",
1,
location,
scope.new_identifier(name),
name,
)
}
}
@ -549,6 +670,34 @@ impl WriteTrace for TraceAsyncReset {
}
}
impl WriteTrace for TracePhantomConst {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgInType {
source_var_type: _,
sink_var_type: _,
duplex_var_type: _,
properties,
scope,
} = arg.in_type();
let Self {
location,
name,
ty: _,
flow: _,
} = self;
write_vcd_var(
properties,
scope,
MemoryElementPartBody::Scalar,
writer,
"string",
1,
location,
name,
)
}
}
impl WriteTrace for TraceSimOnly {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgInType {
@ -566,12 +715,13 @@ impl WriteTrace for TraceSimOnly {
} = self;
write_vcd_var(
properties,
scope,
MemoryElementPartBody::Scalar,
writer,
"string",
1,
location,
scope.new_identifier(name),
name,
)
}
}
@ -595,14 +745,24 @@ impl WriteTrace for TraceScope {
impl WriteTrace for TraceModule {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModule { properties, scope } = arg.module();
let ArgModule {
properties,
scope,
instance_name,
} = arg.module();
let Self { name, children } = self;
write_vcd_scope(writer, "module", name, scope, |writer, scope| {
for child in children {
child.write_trace(writer, ArgModuleBody { properties, scope })?;
}
Ok(())
})
write_vcd_scope(
writer,
"module",
instance_name.unwrap_or(name),
scope,
|writer, scope| {
for child in children {
child.write_trace(writer, ArgModuleBody { properties, scope })?;
}
Ok(())
},
)
}
}
@ -610,7 +770,7 @@ impl WriteTrace for TraceInstance {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties, scope } = arg.module_body();
let Self {
name: _,
name,
instance_io,
module,
ty: _,
@ -622,10 +782,17 @@ impl WriteTrace for TraceInstance {
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
scope,
scope: None,
},
)?;
module.write_trace(writer, ArgModule { properties, scope })
module.write_trace(
writer,
ArgModule {
properties,
scope,
instance_name: Some(name),
},
)
}
}
@ -664,7 +831,7 @@ impl WriteTrace for TraceMem {
sink_var_type: "reg",
duplex_var_type: "reg",
properties,
scope,
scope: Some(scope),
},
)
},
@ -696,7 +863,7 @@ impl WriteTrace for TraceMemPort {
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
scope,
scope: Some(scope),
},
)
}
@ -717,7 +884,7 @@ impl WriteTrace for TraceWire {
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
scope,
scope: Some(scope),
},
)
}
@ -738,7 +905,7 @@ impl WriteTrace for TraceReg {
sink_var_type: "reg",
duplex_var_type: "reg",
properties,
scope,
scope: Some(scope),
},
)
}
@ -760,7 +927,7 @@ impl WriteTrace for TraceModuleIO {
sink_var_type: "wire",
duplex_var_type: "wire",
properties,
scope,
scope: Some(scope),
},
)
}
@ -781,7 +948,7 @@ impl WriteTrace for TraceBundle {
ty: _,
flow: _,
} = self;
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| {
for field in fields {
field.write_trace(
writer,
@ -790,7 +957,7 @@ impl WriteTrace for TraceBundle {
sink_var_type,
duplex_var_type,
properties,
scope,
scope: scope.as_deref_mut(),
},
)?;
}
@ -814,7 +981,7 @@ impl WriteTrace for TraceArray {
ty: _,
flow: _,
} = self;
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| {
for element in elements {
element.write_trace(
writer,
@ -823,7 +990,7 @@ impl WriteTrace for TraceArray {
sink_var_type,
duplex_var_type,
properties,
scope,
scope: scope.as_deref_mut(),
},
)?;
}
@ -848,7 +1015,7 @@ impl WriteTrace for TraceEnumWithFields {
ty: _,
flow: _,
} = self;
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
try_write_vcd_scope(writer, "struct", name, scope, |writer, mut scope| {
discriminant.write_trace(
writer,
ArgInType {
@ -856,7 +1023,7 @@ impl WriteTrace for TraceEnumWithFields {
sink_var_type,
duplex_var_type,
properties,
scope,
scope: scope.as_deref_mut(),
},
)?;
for field in non_empty_fields {
@ -867,7 +1034,7 @@ impl WriteTrace for TraceEnumWithFields {
sink_var_type,
duplex_var_type,
properties,
scope,
scope: scope.as_deref_mut(),
},
)?;
}
@ -893,6 +1060,9 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
writeln!(writer, "$timescale {} $end", vcd_timescale(timescale))?;
let mut properties = VcdWriterProperties {
next_scalar_id: trace_scalar_id_count,
scalar_id_to_vcd_id_map: ScalarIdToVcdIdMapOrBuilder::Builder(
ScalarIdToVcdIdMapBuilder::default(),
),
memory_properties: (0..trace_memory_id_count)
.map(|_| MemoryProperties {
element_parts: Vec::with_capacity(8),
@ -905,9 +1075,17 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
&mut writer,
ArgModule {
properties: &mut properties,
scope: &mut Scope::default(),
scope: &mut Scope::new(PathHash::default()),
instance_name: None,
},
)?;
let ScalarIdToVcdIdMapOrBuilder::Builder(scalar_id_to_vcd_id_map_builder) =
properties.scalar_id_to_vcd_id_map
else {
unreachable!();
};
properties.scalar_id_to_vcd_id_map =
ScalarIdToVcdIdMapOrBuilder::Built(scalar_id_to_vcd_id_map_builder.build());
writeln!(writer, "$enddefinitions $end")?;
writeln!(writer, "$dumpvars")?;
Ok(VcdWriter {
@ -937,8 +1115,100 @@ struct MemoryProperties {
element_index: usize,
}
struct ScalarIdToVcdIdMap {
scalar_id_to_vcd_id_map: Box<[Option<VcdId>]>,
}
#[derive(Default)]
struct ScalarIdToVcdIdMapBuilder {
scalar_id_to_vcd_id_map: BTreeMap<usize, Option<VcdId>>,
lower_half_to_next_upper_half_map: HashMap<u64, u64>,
}
impl ScalarIdToVcdIdMapBuilder {
fn unused_scalar_id(&mut self, scalar_id: usize) {
self.scalar_id_to_vcd_id_map
.entry(scalar_id)
.or_insert(None);
}
/// `VcdId`s are based off of `path_hash` (and not `scalar_id`) since the hash doesn't change
/// when unrelated variables are added/removed, making the generated VCD more friendly for git diff.
fn get_or_insert(&mut self, scalar_id: usize, path_hash: &PathHash) -> VcdId {
*self
.scalar_id_to_vcd_id_map
.entry(scalar_id)
.or_insert(None)
.get_or_insert_with(|| {
let hash = u128::from_le_bytes(
*path_hash
.0
.clone()
.finalize()
.first_chunk()
.expect("known to be bigger than u128"),
);
let lower_half = (hash % VcdId::LOW_HALF_MODULUS as u128) as u64;
let next_upper_half = self
.lower_half_to_next_upper_half_map
.entry(lower_half)
.or_insert(if lower_half == 0 { 1 } else { 0 });
let upper_half = *next_upper_half;
*next_upper_half += 1;
let Some(id) = upper_half
.checked_mul(VcdId::LOW_HALF_MODULUS)
.and_then(|v| v.checked_add(lower_half))
else {
panic!("too many VcdIds");
};
VcdId(NonZeroU64::new(id).expect("known to not be zero"))
})
}
fn build(self) -> ScalarIdToVcdIdMap {
ScalarIdToVcdIdMap {
scalar_id_to_vcd_id_map: self
.scalar_id_to_vcd_id_map
.into_iter()
.enumerate()
.map(|(index, (scalar_id, vcd_id))| {
if index != scalar_id {
panic!("missing scalar id {index}");
}
vcd_id
})
.collect(),
}
}
}
enum ScalarIdToVcdIdMapOrBuilder {
Builder(ScalarIdToVcdIdMapBuilder),
Built(ScalarIdToVcdIdMap),
}
impl ScalarIdToVcdIdMapOrBuilder {
fn built_scalar_id_to_vcd_id(&self, scalar_id: usize) -> Option<VcdId> {
let Self::Built(v) = self else {
panic!("ScalarIdToVcdIdMap isn't built yet");
};
v.scalar_id_to_vcd_id_map[scalar_id]
}
fn builder_get_or_insert(&mut self, scalar_id: usize, path_hash: &PathHash) -> VcdId {
let Self::Builder(v) = self else {
panic!("ScalarIdToVcdIdMap is already built");
};
v.get_or_insert(scalar_id, path_hash)
}
fn builder_unused_scalar_id(&mut self, scalar_id: usize) {
let Self::Builder(v) = self else {
panic!("ScalarIdToVcdIdMap is already built");
};
v.unused_scalar_id(scalar_id)
}
}
struct VcdWriterProperties {
next_scalar_id: usize,
scalar_id_to_vcd_id_map: ScalarIdToVcdIdMapOrBuilder,
memory_properties: Box<[MemoryProperties]>,
}
@ -958,8 +1228,11 @@ impl<W: io::Write + 'static> VcdWriter<W> {
fn write_string_value_change(
writer: &mut impl io::Write,
value: impl fmt::Display,
id: usize,
id: Option<VcdId>,
) -> io::Result<()> {
let Some(id) = id else {
return Ok(());
};
write!(writer, "s{} ", Escaped(value))?;
write_vcd_id(writer, id)?;
writer.write_all(b"\n")
@ -968,8 +1241,11 @@ fn write_string_value_change(
fn write_bits_value_change(
writer: &mut impl io::Write,
value: &BitSlice,
id: usize,
id: Option<VcdId>,
) -> io::Result<()> {
let Some(id) = id else {
return Ok(());
};
match value.len() {
0 => writer.write_all(b"s0 ")?,
1 => writer.write_all(if value[0] { b"1" } else { b"0" })?,
@ -998,7 +1274,7 @@ fn write_enum_discriminant_value_change(
writer: &mut impl io::Write,
variant_index: usize,
ty: Enum,
id: usize,
id: Option<VcdId>,
) -> io::Result<()> {
write_string_value_change(
writer,
@ -1033,7 +1309,9 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
MemoryElementPartBody::Scalar => write_bits_value_change(
&mut self.writer,
&element_data[start..start + len],
first_id + element_index,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(first_id + element_index),
)?,
MemoryElementPartBody::EnumDiscriminant { ty } => {
let mut variant_index = 0;
@ -1043,7 +1321,9 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
&mut self.writer,
variant_index,
*ty,
first_id + element_index,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(first_id + element_index),
)?
}
}
@ -1052,11 +1332,23 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
}
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
write_bits_value_change(&mut self.writer, value, id.as_usize())
write_bits_value_change(
&mut self.writer,
value,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
}
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
write_bits_value_change(&mut self.writer, value, id.as_usize())
write_bits_value_change(
&mut self.writer,
value,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
}
fn finish_init(&mut self) -> Result<(), Self::Error> {
@ -1088,7 +1380,30 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
variant_index: usize,
ty: Enum,
) -> Result<(), Self::Error> {
write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize())
write_enum_discriminant_value_change(
&mut self.writer,
variant_index,
ty,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
}
fn set_signal_phantom_const(
&mut self,
id: TraceScalarId,
ty: PhantomConst,
) -> Result<(), Self::Error> {
// avoid multi-line strings because GTKWave can't display them properly:
// https://github.com/gtkwave/gtkwave/issues/460
write_string_value_change(
&mut self.writer,
format_args!("{ty:?}"),
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
}
fn set_signal_sim_only_value(
@ -1096,7 +1411,13 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
id: TraceScalarId,
value: &DynSimOnlyValue,
) -> Result<(), Self::Error> {
write_string_value_change(&mut self.writer, format_args!("{value:?}"), id.as_usize())
write_string_value_change(
&mut self.writer,
format_args!("{value:?}"),
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
}
}
@ -1121,7 +1442,7 @@ mod tests {
#[test]
fn test_scope() {
let mut scope = Scope::default();
let mut scope = Scope::new(PathHash::default());
assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo");
assert_eq!(
&*scope.new_identifier("foo_0".intern()).unescaped_name,

View file

@ -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();
}
args.run(module).expect("testing::assert_formal() failed");
) -> eyre::Result<JobArgsAndDependencies<ExternalCommandJobKind<Formal>>> {
let args = JobKindAndArgs {
kind: BaseJobKind,
args: BaseJobArgs::from_output_dir_and_env(get_assert_formal_target_path(&test_name), None),
};
let dependencies = JobArgsAndDependencies {
args,
dependencies: (),
};
let args = JobKindAndArgs {
kind: FirrtlJobKind,
args: FirrtlArgs { export_options },
};
let dependencies = JobArgsAndDependencies { args, dependencies };
let args = JobKindAndArgs {
kind: ExternalCommandJobKind::new(),
args: ExternalCommandArgs::resolve_program_path(
None,
UnadjustedVerilogArgs {
firtool_extra_args: vec![],
verilog_dialect: None,
verilog_debug: true,
},
)?,
};
let dependencies = JobArgsAndDependencies { args, dependencies };
let args = JobKindAndArgs {
kind: VerilogJobKind,
args: VerilogJobArgs {},
};
let dependencies = JobArgsAndDependencies { args, dependencies };
let args = JobKindAndArgs {
kind: WriteSbyFileJobKind,
args: FormalArgs {
sby_extra_args: vec![],
formal_mode,
formal_depth,
formal_solver: solver.unwrap_or(FormalArgs::DEFAULT_SOLVER).into(),
smtbmc_extra_args: vec![],
},
};
let dependencies = JobArgsAndDependencies { args, dependencies };
let args = JobKindAndArgs {
kind: ExternalCommandJobKind::new(),
args: ExternalCommandArgs::resolve_program_path(None, FormalAdditionalArgs {})?,
};
Ok(JobArgsAndDependencies { args, dependencies })
}
pub fn try_assert_formal<M: AsRef<Module<T>>, T: BundleType>(
test_name: impl std::fmt::Display,
module: M,
formal_mode: FormalMode,
formal_depth: u64,
solver: Option<&str>,
export_options: ExportOptions,
) -> eyre::Result<()> {
const APP_NAME: &'static str = "fayalite::testing::assert_formal";
make_assert_formal_args(
&test_name,
formal_mode,
formal_depth,
solver,
export_options,
)?
.run_without_platform(
|NoArgs {}| Ok(JobParams::new(module)),
&GlobalParams::new(None, APP_NAME),
)
}
#[track_caller]
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");
}

View file

@ -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},
};
@ -367,7 +367,15 @@ impl<D: Type> TypeOrDefault<D> for crate::__ {
}
pub trait Type:
Copy + Hash + Eq + fmt::Debug + Send + Sync + 'static + FillInDefaultedGenerics<Type = Self>
Copy
+ Hash
+ Eq
+ fmt::Debug
+ Send
+ Sync
+ 'static
+ FillInDefaultedGenerics<Type = Self>
+ SimValueDebug
{
type BaseType: BaseType;
type MaskType: Type<MaskType = Self::MaskType>;
@ -402,6 +410,16 @@ pub trait Type:
) -> OpaqueSimValueWritten<'w>;
}
pub trait SimValueDebug {
fn sim_value_debug(value: &<Self as Type>::SimValue, f: &mut fmt::Formatter<'_>) -> fmt::Result
where
Self: Type;
}
pub trait SimValueDisplay: Type {
fn sim_value_display(value: &Self::SimValue, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
pub trait BaseType:
Type<
BaseType = Self,
@ -490,6 +508,15 @@ impl Type for CanonicalType {
}
}
impl SimValueDebug for CanonicalType {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(value, f)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Default)]
#[non_exhaustive]
pub struct OpaqueSimValueSizeRange {

View file

@ -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),
}

View file

@ -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, copy_le_bytes_to_bitslice};
pub mod job_server;
pub mod prefix_sum;

View file

@ -1,192 +1,156 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use ctor::ctor;
use jobslot::{Acquired, Client};
use ctor::{ctor, dtor};
use jobslot::Client;
use std::{
ffi::OsString,
mem,
io, mem,
num::NonZeroUsize,
sync::{Condvar, Mutex, Once, OnceLock},
thread::spawn,
sync::{Mutex, MutexGuard},
};
fn get_or_make_client() -> &'static Client {
#[ctor]
static CLIENT: OnceLock<Client> = unsafe {
match Client::from_env() {
Some(client) => OnceLock::from(client),
None => OnceLock::new(),
}
};
#[ctor]
static CLIENT: Mutex<Option<Option<Client>>> = unsafe { Mutex::new(Some(Client::from_env())) };
CLIENT.get_or_init(|| {
let mut available_parallelism = None;
let mut args = std::env::args_os().skip(1);
while let Some(arg) = args.next() {
const TEST_THREADS_OPTION: &'static [u8] = b"--test-threads";
if arg.as_encoded_bytes().starts_with(TEST_THREADS_OPTION) {
match arg.as_encoded_bytes().get(TEST_THREADS_OPTION.len()) {
Some(b'=') => {
let mut arg = arg.into_encoded_bytes();
arg.drain(..=TEST_THREADS_OPTION.len());
available_parallelism = Some(arg);
break;
#[dtor]
fn drop_client() {
drop(
match CLIENT.lock() {
Ok(v) => v,
Err(e) => e.into_inner(),
}
.take(),
);
}
fn get_or_make_client() -> Client {
CLIENT
.lock()
.expect("shouldn't have panicked")
.as_mut()
.expect("shutting down")
.get_or_insert_with(|| {
let mut available_parallelism = None;
let mut args = std::env::args_os().skip(1);
while let Some(arg) = args.next() {
const TEST_THREADS_OPTION: &'static [u8] = b"--test-threads";
if arg.as_encoded_bytes().starts_with(TEST_THREADS_OPTION) {
match arg.as_encoded_bytes().get(TEST_THREADS_OPTION.len()) {
Some(b'=') => {
let mut arg = arg.into_encoded_bytes();
arg.drain(..=TEST_THREADS_OPTION.len());
available_parallelism = Some(arg);
break;
}
None => {
available_parallelism = args.next().map(OsString::into_encoded_bytes);
break;
}
_ => {}
}
None => {
available_parallelism = args.next().map(OsString::into_encoded_bytes);
break;
}
_ => {}
}
}
}
let available_parallelism = if let Some(available_parallelism) = available_parallelism
.as_deref()
.and_then(|v| std::str::from_utf8(v).ok())
.and_then(|v| v.parse().ok())
{
available_parallelism
} else if let Ok(available_parallelism) = std::thread::available_parallelism() {
available_parallelism
} else {
NonZeroUsize::new(1).unwrap()
};
Client::new_with_fifo(available_parallelism.get() - 1).expect("failed to create job server")
})
let available_parallelism = if let Some(available_parallelism) = available_parallelism
.as_deref()
.and_then(|v| std::str::from_utf8(v).ok())
.and_then(|v| v.parse().ok())
{
available_parallelism
} else if let Ok(available_parallelism) = std::thread::available_parallelism() {
available_parallelism
} else {
NonZeroUsize::new(1).unwrap()
};
Client::new_with_fifo(available_parallelism.get() - 1)
.expect("failed to create job server")
})
.clone()
}
struct State {
obtained_count: usize,
waiting_count: usize,
available: Vec<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;
let client = get_or_make_client();
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
} else {
drop(state);
acquired = Some(
client
.acquire()
.expect("can't acquire token from job server"),
);
STATE.lock().unwrap()
};
}
});
});
}
fn acquire_inner(block: bool) -> Option<Self> {
Self::start_acquire_thread();
let mut state = STATE.lock().unwrap();
loop {
if let Some(acquired) = state.available.pop() {
return Some(Self {
job: AcquiredJobInner::FromJobServer(acquired),
});
state.waiting_count -= 1;
state
}
if state.implicit_available {
state.implicit_available = false;
return Some(Self {
job: AcquiredJobInner::ImplicitJob,
});
}
if !block {
return None;
}
state.waiting_count += 1;
state = COND_VAR.wait(state).unwrap();
state.waiting_count -= 1;
}
}
pub fn try_acquire() -> Option<Self> {
Self::acquire_inner(false)
}
pub fn acquire() -> Self {
Self::acquire_inner(true).expect("failed to acquire token")
impl Drop for Waiting {
fn drop(&mut self) {
STATE.lock().unwrap().waiting_count -= 1;
}
}
let mut state = STATE.lock().unwrap();
if state.obtained_count == 0 && state.waiting_count == 0 {
state.obtained_count = 1; // get implicit token
return Ok(Self { client });
}
state.waiting_count += 1;
drop(state);
let waiting = Waiting {};
client.acquire_raw()?;
state = waiting.done();
state.obtained_count = state
.obtained_count
.checked_add(1)
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "obtained_count overflowed"))?;
drop(state);
Ok(Self { client })
}
pub fn run_command<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
}
}
}

View file

@ -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,410 @@ 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
}
}
pub(crate) fn copy_le_bytes_to_bitslice(
dest: &mut BitSlice<usize, Lsb0>,
bytes: &[u8],
msb_fill: bool,
) {
let (chunks, remainder) = bytes.as_chunks();
let mut filled_to = 0;
for (i, chunk) in chunks.iter().enumerate() {
if let Some(start_bit_index) = i.checked_mul(usize::BITS as usize)
&& start_bit_index < dest.len()
{
let end_bit_index = start_bit_index
.saturating_add(usize::BITS as usize)
.min(dest.len());
let bit_len = end_bit_index - start_bit_index;
let chunk = usize::from_le_bytes(*chunk);
dest[start_bit_index..end_bit_index].copy_from_bitslice(&chunk.view_bits()[..bit_len]);
filled_to = end_bit_index;
} else {
break;
}
}
if !remainder.is_empty() {
if let Some(start_bit_index) = chunks.len().checked_mul(usize::BITS as usize)
&& start_bit_index < dest.len()
{
let end_bit_index = start_bit_index
.saturating_add(usize::BITS as usize)
.min(dest.len());
let bit_len = end_bit_index - start_bit_index;
let mut chunk = [if msb_fill { !0 } else { 0 }; _];
chunk[..remainder.len()].copy_from_slice(remainder);
let chunk = usize::from_le_bytes(chunk);
dest[start_bit_index..end_bit_index].copy_from_bitslice(&chunk.view_bits()[..bit_len]);
filled_to = end_bit_index;
}
}
dest[filled_to..].fill(msb_fill);
}

View file

@ -25,7 +25,7 @@ impl<T: Type> ReadyValid<T> {
#[hdl]
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;

View 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
View 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())
}

View 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))
}

View 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();
}

File diff suppressed because it is too large Load diff

View file

@ -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,

View file

@ -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(),
);
}
}

View file

@ -4,7 +4,6 @@ use fayalite::{
bundle::BundleType,
enum_::EnumType,
int::{BoolOrIntType, IntType},
phantom_const::PhantomConst,
prelude::*,
ty::StaticType,
};
@ -197,3 +196,61 @@ 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,
}
#[hdl(outline_generated)]
pub enum EnumWithOnlyOneVariant {
A,
}
#[hdl(outline_generated)]
pub enum EnumWithOnlyOneVariant2<T> {
A(T),
}

View file

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

View file

@ -6,13 +6,14 @@ use fayalite::{
int::{UIntInRange, UIntInRangeInclusive},
intern::Intern,
module::transform::simplify_enums::SimplifyEnumsKind,
platform::PlatformIOBuilder,
prelude::*,
reset::ResetType,
ty::StaticType,
};
use serde_json::json;
#[hdl(outline_generated)]
#[hdl(outline_generated, cmp_eq)]
pub enum TestEnum {
A,
B(UInt<8>),
@ -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]
@ -678,6 +679,366 @@ circuit check_enum_literals:
};
}
#[hdl_module(outline_generated)]
pub fn check_enum_cmp_eq() {
#[hdl]
let lhs: TestEnum = m.input();
#[hdl]
let rhs: TestEnum = m.input();
#[hdl]
let eq: Bool = m.output();
connect(eq, lhs.cmp_eq(rhs));
}
#[test]
fn test_enum_cmp_eq() {
let _n = SourceLocation::normalize_files_for_tests();
let m = check_enum_cmp_eq();
dbg!(m);
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_enums: None,
..ExportOptions::default()
},
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_enum_cmp_eq:
type Ty0 = {|A, B: UInt<8>, C: UInt<1>[3]|}
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
input rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
wire TestEnum_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 5:1]
connect TestEnum_cmp_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 5:1]
match lhs: @[module-XXXXXXXXXX.rs 5:1]
A:
match rhs: @[module-XXXXXXXXXX.rs 5:1]
A:
connect TestEnum_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 5:1]
B(_match_arm_value):
skip
C(_match_arm_value_1):
skip
B(_match_arm_value_2):
match rhs: @[module-XXXXXXXXXX.rs 5:1]
A:
skip
B(_match_arm_value_3):
connect TestEnum_cmp_eq, eq(_match_arm_value_2, _match_arm_value_3) @[module-XXXXXXXXXX.rs 5:1]
C(_match_arm_value_4):
skip
C(_match_arm_value_5):
match rhs: @[module-XXXXXXXXXX.rs 5:1]
A:
skip
B(_match_arm_value_6):
skip
C(_match_arm_value_7):
wire _array_literal_expr: UInt<1>[3]
connect _array_literal_expr[0], eq(_match_arm_value_5[0], _match_arm_value_7[0])
connect _array_literal_expr[1], eq(_match_arm_value_5[1], _match_arm_value_7[1])
connect _array_literal_expr[2], eq(_match_arm_value_5[2], _match_arm_value_7[2])
wire _cast_array_to_bits_expr: UInt<1>[3]
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
wire _cast_to_bits_expr: UInt<3>
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
connect TestEnum_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
connect eq, TestEnum_cmp_eq @[module-XXXXXXXXXX.rs 6:1]
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody),
..ExportOptions::default()
},
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_enum_cmp_eq:
type Ty0 = {|A, B, C|}
type Ty1 = {tag: Ty0, body: UInt<8>}
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input lhs: Ty1 @[module-XXXXXXXXXX.rs 2:1]
input rhs: Ty1 @[module-XXXXXXXXXX.rs 3:1]
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
wire TestEnum_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 5:1]
connect TestEnum_cmp_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 5:1]
match lhs.tag: @[module-XXXXXXXXXX.rs 5:1]
A:
match rhs.tag: @[module-XXXXXXXXXX.rs 5:1]
A:
connect TestEnum_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 5:1]
B:
skip
C:
skip
B:
match rhs.tag: @[module-XXXXXXXXXX.rs 5:1]
A:
skip
B:
connect TestEnum_cmp_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 5:1]
C:
skip
C:
match rhs.tag: @[module-XXXXXXXXXX.rs 5:1]
A:
skip
B:
skip
C:
wire _array_literal_expr: UInt<1>[3]
wire _cast_bits_to_array_expr: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
wire _cast_bits_to_array_expr_1: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
connect _array_literal_expr[0], eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0])
wire _cast_bits_to_array_expr_2: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_2: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_2[0], bits(bits(lhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_2[0], _cast_bits_to_array_expr_flattened_2[0]
connect _cast_bits_to_array_expr_flattened_2[1], bits(bits(lhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_2[1], _cast_bits_to_array_expr_flattened_2[1]
connect _cast_bits_to_array_expr_flattened_2[2], bits(bits(lhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_2[2], _cast_bits_to_array_expr_flattened_2[2]
wire _cast_bits_to_array_expr_3: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_3: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_3[0], bits(bits(rhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_3[0], _cast_bits_to_array_expr_flattened_3[0]
connect _cast_bits_to_array_expr_flattened_3[1], bits(bits(rhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_3[1], _cast_bits_to_array_expr_flattened_3[1]
connect _cast_bits_to_array_expr_flattened_3[2], bits(bits(rhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_3[2], _cast_bits_to_array_expr_flattened_3[2]
connect _array_literal_expr[1], eq(_cast_bits_to_array_expr_2[1], _cast_bits_to_array_expr_3[1])
wire _cast_bits_to_array_expr_4: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_4: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_4[0], bits(bits(lhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_4[0], _cast_bits_to_array_expr_flattened_4[0]
connect _cast_bits_to_array_expr_flattened_4[1], bits(bits(lhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_4[1], _cast_bits_to_array_expr_flattened_4[1]
connect _cast_bits_to_array_expr_flattened_4[2], bits(bits(lhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_4[2], _cast_bits_to_array_expr_flattened_4[2]
wire _cast_bits_to_array_expr_5: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_5: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_5[0], bits(bits(rhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_5[0], _cast_bits_to_array_expr_flattened_5[0]
connect _cast_bits_to_array_expr_flattened_5[1], bits(bits(rhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_5[1], _cast_bits_to_array_expr_flattened_5[1]
connect _cast_bits_to_array_expr_flattened_5[2], bits(bits(rhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_5[2], _cast_bits_to_array_expr_flattened_5[2]
connect _array_literal_expr[2], eq(_cast_bits_to_array_expr_4[2], _cast_bits_to_array_expr_5[2])
wire _cast_array_to_bits_expr: UInt<1>[3]
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
wire _cast_to_bits_expr: UInt<3>
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
connect TestEnum_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
connect eq, TestEnum_cmp_eq @[module-XXXXXXXXXX.rs 6:1]
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
..ExportOptions::default()
},
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_enum_cmp_eq:
type Ty0 = {tag: UInt<2>, body: UInt<8>}
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
input rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
wire TestEnum_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 5:1]
connect TestEnum_cmp_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 5:1]
when eq(lhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1]
when eq(rhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1]
connect TestEnum_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 5:1]
else when eq(rhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1]
skip
else when eq(lhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1]
when eq(rhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1]
skip
else when eq(rhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1]
connect TestEnum_cmp_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 5:1]
else when eq(rhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1]
skip
else when eq(rhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1]
skip
else:
wire _array_literal_expr: UInt<1>[3]
wire _cast_bits_to_array_expr: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
wire _cast_bits_to_array_expr_1: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
connect _array_literal_expr[0], eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0])
wire _cast_bits_to_array_expr_2: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_2: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_2[0], bits(bits(lhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_2[0], _cast_bits_to_array_expr_flattened_2[0]
connect _cast_bits_to_array_expr_flattened_2[1], bits(bits(lhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_2[1], _cast_bits_to_array_expr_flattened_2[1]
connect _cast_bits_to_array_expr_flattened_2[2], bits(bits(lhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_2[2], _cast_bits_to_array_expr_flattened_2[2]
wire _cast_bits_to_array_expr_3: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_3: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_3[0], bits(bits(rhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_3[0], _cast_bits_to_array_expr_flattened_3[0]
connect _cast_bits_to_array_expr_flattened_3[1], bits(bits(rhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_3[1], _cast_bits_to_array_expr_flattened_3[1]
connect _cast_bits_to_array_expr_flattened_3[2], bits(bits(rhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_3[2], _cast_bits_to_array_expr_flattened_3[2]
connect _array_literal_expr[1], eq(_cast_bits_to_array_expr_2[1], _cast_bits_to_array_expr_3[1])
wire _cast_bits_to_array_expr_4: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_4: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_4[0], bits(bits(lhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_4[0], _cast_bits_to_array_expr_flattened_4[0]
connect _cast_bits_to_array_expr_flattened_4[1], bits(bits(lhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_4[1], _cast_bits_to_array_expr_flattened_4[1]
connect _cast_bits_to_array_expr_flattened_4[2], bits(bits(lhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_4[2], _cast_bits_to_array_expr_flattened_4[2]
wire _cast_bits_to_array_expr_5: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_5: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_5[0], bits(bits(rhs.body, 2, 0), 0, 0)
connect _cast_bits_to_array_expr_5[0], _cast_bits_to_array_expr_flattened_5[0]
connect _cast_bits_to_array_expr_flattened_5[1], bits(bits(rhs.body, 2, 0), 1, 1)
connect _cast_bits_to_array_expr_5[1], _cast_bits_to_array_expr_flattened_5[1]
connect _cast_bits_to_array_expr_flattened_5[2], bits(bits(rhs.body, 2, 0), 2, 2)
connect _cast_bits_to_array_expr_5[2], _cast_bits_to_array_expr_flattened_5[2]
connect _array_literal_expr[2], eq(_cast_bits_to_array_expr_4[2], _cast_bits_to_array_expr_5[2])
wire _cast_array_to_bits_expr: UInt<1>[3]
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
wire _cast_to_bits_expr: UInt<3>
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
connect TestEnum_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
connect eq, TestEnum_cmp_eq @[module-XXXXXXXXXX.rs 6:1]
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithUInt),
..ExportOptions::default()
},
"/test/check_enum_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_enum_cmp_eq:
module check_enum_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input lhs: UInt<10> @[module-XXXXXXXXXX.rs 2:1]
input rhs: UInt<10> @[module-XXXXXXXXXX.rs 3:1]
output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
wire TestEnum_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 5:1]
connect TestEnum_cmp_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 5:1]
when eq(bits(lhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1]
when eq(bits(rhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1]
connect TestEnum_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 5:1]
else when eq(bits(rhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1]
skip
else when eq(bits(lhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1]
when eq(bits(rhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1]
skip
else when eq(bits(rhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1]
connect TestEnum_cmp_eq, eq(bits(bits(lhs, 9, 2), 7, 0), bits(bits(rhs, 9, 2), 7, 0)) @[module-XXXXXXXXXX.rs 5:1]
else when eq(bits(rhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1]
skip
else when eq(bits(rhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1]
skip
else:
wire _array_literal_expr: UInt<1>[3]
wire _cast_bits_to_array_expr: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened[0], bits(bits(bits(lhs, 9, 2), 2, 0), 0, 0)
connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0]
connect _cast_bits_to_array_expr_flattened[1], bits(bits(bits(lhs, 9, 2), 2, 0), 1, 1)
connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1]
connect _cast_bits_to_array_expr_flattened[2], bits(bits(bits(lhs, 9, 2), 2, 0), 2, 2)
connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2]
wire _cast_bits_to_array_expr_1: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(bits(rhs, 9, 2), 2, 0), 0, 0)
connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0]
connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(bits(rhs, 9, 2), 2, 0), 1, 1)
connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1]
connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(bits(rhs, 9, 2), 2, 0), 2, 2)
connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2]
connect _array_literal_expr[0], eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0])
wire _cast_bits_to_array_expr_2: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_2: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_2[0], bits(bits(bits(lhs, 9, 2), 2, 0), 0, 0)
connect _cast_bits_to_array_expr_2[0], _cast_bits_to_array_expr_flattened_2[0]
connect _cast_bits_to_array_expr_flattened_2[1], bits(bits(bits(lhs, 9, 2), 2, 0), 1, 1)
connect _cast_bits_to_array_expr_2[1], _cast_bits_to_array_expr_flattened_2[1]
connect _cast_bits_to_array_expr_flattened_2[2], bits(bits(bits(lhs, 9, 2), 2, 0), 2, 2)
connect _cast_bits_to_array_expr_2[2], _cast_bits_to_array_expr_flattened_2[2]
wire _cast_bits_to_array_expr_3: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_3: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_3[0], bits(bits(bits(rhs, 9, 2), 2, 0), 0, 0)
connect _cast_bits_to_array_expr_3[0], _cast_bits_to_array_expr_flattened_3[0]
connect _cast_bits_to_array_expr_flattened_3[1], bits(bits(bits(rhs, 9, 2), 2, 0), 1, 1)
connect _cast_bits_to_array_expr_3[1], _cast_bits_to_array_expr_flattened_3[1]
connect _cast_bits_to_array_expr_flattened_3[2], bits(bits(bits(rhs, 9, 2), 2, 0), 2, 2)
connect _cast_bits_to_array_expr_3[2], _cast_bits_to_array_expr_flattened_3[2]
connect _array_literal_expr[1], eq(_cast_bits_to_array_expr_2[1], _cast_bits_to_array_expr_3[1])
wire _cast_bits_to_array_expr_4: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_4: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_4[0], bits(bits(bits(lhs, 9, 2), 2, 0), 0, 0)
connect _cast_bits_to_array_expr_4[0], _cast_bits_to_array_expr_flattened_4[0]
connect _cast_bits_to_array_expr_flattened_4[1], bits(bits(bits(lhs, 9, 2), 2, 0), 1, 1)
connect _cast_bits_to_array_expr_4[1], _cast_bits_to_array_expr_flattened_4[1]
connect _cast_bits_to_array_expr_flattened_4[2], bits(bits(bits(lhs, 9, 2), 2, 0), 2, 2)
connect _cast_bits_to_array_expr_4[2], _cast_bits_to_array_expr_flattened_4[2]
wire _cast_bits_to_array_expr_5: UInt<1>[3]
wire _cast_bits_to_array_expr_flattened_5: UInt<1>[3]
connect _cast_bits_to_array_expr_flattened_5[0], bits(bits(bits(rhs, 9, 2), 2, 0), 0, 0)
connect _cast_bits_to_array_expr_5[0], _cast_bits_to_array_expr_flattened_5[0]
connect _cast_bits_to_array_expr_flattened_5[1], bits(bits(bits(rhs, 9, 2), 2, 0), 1, 1)
connect _cast_bits_to_array_expr_5[1], _cast_bits_to_array_expr_flattened_5[1]
connect _cast_bits_to_array_expr_flattened_5[2], bits(bits(bits(rhs, 9, 2), 2, 0), 2, 2)
connect _cast_bits_to_array_expr_5[2], _cast_bits_to_array_expr_flattened_5[2]
connect _array_literal_expr[2], eq(_cast_bits_to_array_expr_4[2], _cast_bits_to_array_expr_5[2])
wire _cast_array_to_bits_expr: UInt<1>[3]
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
wire _cast_to_bits_expr: UInt<3>
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
connect TestEnum_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
connect eq, TestEnum_cmp_eq @[module-XXXXXXXXXX.rs 6:1]
",
};
}
#[hdl_module(outline_generated)]
pub fn check_struct_enum_match() {
#[hdl]
@ -1175,7 +1536,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 +2144,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 +2314,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 +2629,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 +4993,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]
",
};
}

File diff suppressed because it is too large Load diff

View file

@ -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: [
@ -1218,6 +1218,7 @@ Simulation {
index: StatePartIndex<BigSlots>(0),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xff,
last_state: 0xff,
},
@ -1227,6 +1228,7 @@ Simulation {
index: StatePartIndex<BigSlots>(1),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x7f,
last_state: 0x7f,
},
@ -1236,6 +1238,7 @@ Simulation {
index: StatePartIndex<BigSlots>(2),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x3f,
last_state: 0x3f,
},
@ -1245,6 +1248,7 @@ Simulation {
index: StatePartIndex<BigSlots>(3),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x1f,
last_state: 0x1f,
},
@ -1254,6 +1258,7 @@ Simulation {
index: StatePartIndex<BigSlots>(4),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x0f,
last_state: 0x0f,
},
@ -1263,6 +1268,7 @@ Simulation {
index: StatePartIndex<BigSlots>(5),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x07,
last_state: 0x07,
},
@ -1272,6 +1278,7 @@ Simulation {
index: StatePartIndex<BigSlots>(6),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x03,
last_state: 0x03,
},
@ -1281,6 +1288,7 @@ Simulation {
index: StatePartIndex<BigSlots>(7),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x01,
last_state: 0x01,
},
@ -1290,6 +1298,7 @@ Simulation {
index: StatePartIndex<BigSlots>(8),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x00,
last_state: 0x00,
},
@ -1299,6 +1308,7 @@ Simulation {
index: StatePartIndex<BigSlots>(9),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x80,
last_state: 0x80,
},
@ -1308,6 +1318,7 @@ Simulation {
index: StatePartIndex<BigSlots>(10),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xc0,
last_state: 0xc0,
},
@ -1317,6 +1328,7 @@ Simulation {
index: StatePartIndex<BigSlots>(11),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xe0,
last_state: 0xe0,
},
@ -1326,6 +1338,7 @@ Simulation {
index: StatePartIndex<BigSlots>(12),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xf0,
last_state: 0xf0,
},
@ -1335,6 +1348,7 @@ Simulation {
index: StatePartIndex<BigSlots>(13),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xf8,
last_state: 0xf8,
},
@ -1344,6 +1358,7 @@ Simulation {
index: StatePartIndex<BigSlots>(14),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xfc,
last_state: 0xfc,
},
@ -1353,6 +1368,7 @@ Simulation {
index: StatePartIndex<BigSlots>(15),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xfe,
last_state: 0xfe,
},
@ -1362,6 +1378,7 @@ Simulation {
index: StatePartIndex<BigSlots>(16),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xff,
last_state: 0xff,
},
@ -1371,6 +1388,7 @@ Simulation {
index: StatePartIndex<BigSlots>(17),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x7f,
last_state: 0x7f,
},
@ -1380,6 +1398,7 @@ Simulation {
index: StatePartIndex<BigSlots>(18),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x3f,
last_state: 0x3f,
},
@ -1389,6 +1408,7 @@ Simulation {
index: StatePartIndex<BigSlots>(19),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x1f,
last_state: 0x1f,
},
@ -1398,6 +1418,7 @@ Simulation {
index: StatePartIndex<BigSlots>(20),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x0f,
last_state: 0x0f,
},
@ -1407,6 +1428,7 @@ Simulation {
index: StatePartIndex<BigSlots>(21),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x07,
last_state: 0x07,
},
@ -1416,6 +1438,7 @@ Simulation {
index: StatePartIndex<BigSlots>(22),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x03,
last_state: 0x03,
},
@ -1425,6 +1448,7 @@ Simulation {
index: StatePartIndex<BigSlots>(23),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x01,
last_state: 0x01,
},
@ -1434,6 +1458,7 @@ Simulation {
index: StatePartIndex<BigSlots>(24),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x00,
last_state: 0x00,
},
@ -1443,6 +1468,7 @@ Simulation {
index: StatePartIndex<BigSlots>(25),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x80,
last_state: 0x80,
},
@ -1452,6 +1478,7 @@ Simulation {
index: StatePartIndex<BigSlots>(26),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xc0,
last_state: 0xc0,
},
@ -1461,6 +1488,7 @@ Simulation {
index: StatePartIndex<BigSlots>(27),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xe0,
last_state: 0xe0,
},
@ -1470,6 +1498,7 @@ Simulation {
index: StatePartIndex<BigSlots>(28),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xf0,
last_state: 0xf0,
},
@ -1479,6 +1508,7 @@ Simulation {
index: StatePartIndex<BigSlots>(29),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xf8,
last_state: 0xf8,
},
@ -1488,6 +1518,7 @@ Simulation {
index: StatePartIndex<BigSlots>(30),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xfc,
last_state: 0xfc,
},
@ -1497,6 +1528,7 @@ Simulation {
index: StatePartIndex<BigSlots>(31),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xfe,
last_state: 0xe1,
},
@ -1506,6 +1538,7 @@ Simulation {
index: StatePartIndex<BigSlots>(32),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x00,
last_state: 0x00,
},
@ -1515,6 +1548,7 @@ Simulation {
index: StatePartIndex<BigSlots>(33),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xff,
last_state: 0xff,
},
@ -1524,6 +1558,7 @@ Simulation {
index: StatePartIndex<BigSlots>(34),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x10,
last_state: 0x0f,
},
@ -1533,6 +1568,7 @@ Simulation {
index: StatePartIndex<BigSlots>(35),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x00,
last_state: 0xe1,
},
@ -1541,6 +1577,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(36),
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
},
@ -1550,6 +1587,7 @@ Simulation {
index: StatePartIndex<BigSlots>(37),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xff,
last_state: 0xff,
},
@ -1559,6 +1597,7 @@ Simulation {
index: StatePartIndex<BigSlots>(38),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x7f,
last_state: 0x7f,
},
@ -1568,6 +1607,7 @@ Simulation {
index: StatePartIndex<BigSlots>(39),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x3f,
last_state: 0x3f,
},
@ -1577,6 +1617,7 @@ Simulation {
index: StatePartIndex<BigSlots>(40),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x1f,
last_state: 0x1f,
},
@ -1586,6 +1627,7 @@ Simulation {
index: StatePartIndex<BigSlots>(41),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x0f,
last_state: 0x0f,
},
@ -1595,6 +1637,7 @@ Simulation {
index: StatePartIndex<BigSlots>(42),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x07,
last_state: 0x07,
},
@ -1604,6 +1647,7 @@ Simulation {
index: StatePartIndex<BigSlots>(43),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x03,
last_state: 0x03,
},
@ -1613,6 +1657,7 @@ Simulation {
index: StatePartIndex<BigSlots>(44),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x01,
last_state: 0x01,
},
@ -1622,6 +1667,7 @@ Simulation {
index: StatePartIndex<BigSlots>(45),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x00,
last_state: 0x00,
},
@ -1631,6 +1677,7 @@ Simulation {
index: StatePartIndex<BigSlots>(46),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x80,
last_state: 0x80,
},
@ -1640,6 +1687,7 @@ Simulation {
index: StatePartIndex<BigSlots>(47),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xc0,
last_state: 0xc0,
},
@ -1649,6 +1697,7 @@ Simulation {
index: StatePartIndex<BigSlots>(48),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xe0,
last_state: 0xe0,
},
@ -1658,6 +1707,7 @@ Simulation {
index: StatePartIndex<BigSlots>(49),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xf0,
last_state: 0xf0,
},
@ -1667,6 +1717,7 @@ Simulation {
index: StatePartIndex<BigSlots>(50),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xf8,
last_state: 0xf8,
},
@ -1676,6 +1727,7 @@ Simulation {
index: StatePartIndex<BigSlots>(51),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xfc,
last_state: 0xfc,
},
@ -1685,6 +1737,7 @@ Simulation {
index: StatePartIndex<BigSlots>(52),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xfe,
last_state: 0xe1,
},
@ -1699,7 +1752,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: {},
..
}

View file

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

View file

@ -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: [
@ -155,6 +155,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(0),
},
maybe_changed: true,
state: 0x1,
last_state: 0x0,
},
@ -163,6 +164,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(1),
},
maybe_changed: true,
state: 0x0,
last_state: 0x1,
},
@ -177,7 +179,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: {},
..
}

View file

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

View file

@ -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: [
@ -124,13 +124,19 @@ Simulation {
index: StatePartIndex<BigSlots>(0),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x05,
last_state: 0x05,
},
],
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: {},
..
}

View file

@ -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: [
@ -175,6 +175,7 @@ Simulation {
kind: BigAsyncReset {
index: StatePartIndex<BigSlots>(0),
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
},
@ -183,6 +184,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(1),
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
},
@ -197,7 +199,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: {},
..
}

View file

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

View file

@ -100,81 +100,85 @@ 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> },
},
8: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
src: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:1:1
6: Add {
9: 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 {
10: 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 {
11: 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 {
12: 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,
13: BranchIfSmallNonZero {
target: 17,
value: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
13: BranchIfSmallZero {
target: 17,
14: BranchIfSmallZero {
target: 18,
value: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
14: Copy {
15: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
},
15: Branch {
target: 17,
16: Branch {
target: 18,
},
16: Copy {
17: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
},
17: XorSmallImmediate {
18: XorSmallImmediate {
dest: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:1:1
18: Return,
19: Return,
],
..
},
pc: 18,
pc: 19,
memory_write_log: [],
memories: StatePart {
value: [],
@ -261,9 +265,9 @@ Simulation {
}.count,
},
did_initial_settle: true,
clocks_for_past: {},
},
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "counter",
children: [
@ -328,14 +332,16 @@ Simulation {
kind: BigClock {
index: StatePartIndex<BigSlots>(0),
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
last_state: 0x0,
},
SimTrace {
id: TraceScalarId(1),
kind: BigAsyncReset {
index: StatePartIndex<BigSlots>(1),
},
maybe_changed: true,
state: 0x0,
last_state: 0x0,
},
@ -345,6 +351,7 @@ Simulation {
index: StatePartIndex<BigSlots>(2),
ty: UInt<4>,
},
maybe_changed: true,
state: 0x3,
last_state: 0x2,
},
@ -354,8 +361,9 @@ Simulation {
index: StatePartIndex<BigSlots>(3),
ty: UInt<4>,
},
maybe_changed: true,
state: 0x3,
last_state: 0x3,
last_state: 0x2,
},
],
trace_memories: {},
@ -368,9 +376,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: {},
..
}

View file

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

View file

@ -102,61 +102,65 @@ Simulation {
src: StatePartIndex<BigSlots>(7), // (0x4) SlotDebugData { name: "", ty: UInt<5> },
dest_width: 4,
},
// at: module-XXXXXXXXXX.rs:4:1
4: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
src: StatePartIndex<BigSlots>(8), // (0x4) SlotDebugData { name: "", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:3:1
5: IsNonZeroDestIsSmall {
4: IsNonZeroDestIsSmall {
dest: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::cd.rst", ty: SyncReset },
},
// at: module-XXXXXXXXXX.rs:1:1
6: Const {
dest: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
value: 0x3,
},
// at: module-XXXXXXXXXX.rs:3:1
7: IsNonZeroDestIsSmall {
5: 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 {
6: AndSmall {
dest: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
rhs: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
9: BranchIfSmallZero {
target: 14,
7: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
src: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:4:1
8: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
src: StatePartIndex<BigSlots>(8), // (0x4) SlotDebugData { name: "", ty: UInt<4> },
},
// at: module-XXXXXXXXXX.rs:1:1
9: Const {
dest: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
value: 0x3,
},
// at: module-XXXXXXXXXX.rs:3:1
10: BranchIfSmallZero {
target: 15,
value: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
10: BranchIfSmallNonZero {
target: 13,
11: BranchIfSmallNonZero {
target: 14,
value: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
11: Copy {
12: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(4), // (0x4) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg$next", ty: UInt<4> },
},
12: Branch {
target: 14,
13: Branch {
target: 15,
},
13: Copy {
14: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x3) SlotDebugData { name: "InstantiatedModule(counter: counter).counter::count_reg", ty: UInt<4> },
src: StatePartIndex<BigSlots>(5), // (0x3) SlotDebugData { name: "", ty: UInt<4> },
},
14: XorSmallImmediate {
15: XorSmallImmediate {
dest: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x1 1) SlotDebugData { name: "", ty: Bool },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:1:1
15: Return,
16: Return,
],
..
},
pc: 15,
pc: 16,
memory_write_log: [],
memories: StatePart {
value: [],
@ -242,9 +246,9 @@ Simulation {
}.count,
},
did_initial_settle: true,
clocks_for_past: {},
},
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "counter",
children: [
@ -309,14 +313,16 @@ Simulation {
kind: BigClock {
index: StatePartIndex<BigSlots>(0),
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
last_state: 0x0,
},
SimTrace {
id: TraceScalarId(1),
kind: BigSyncReset {
index: StatePartIndex<BigSlots>(1),
},
maybe_changed: false,
state: 0x0,
last_state: 0x0,
},
@ -326,6 +332,7 @@ Simulation {
index: StatePartIndex<BigSlots>(2),
ty: UInt<4>,
},
maybe_changed: true,
state: 0x3,
last_state: 0x2,
},
@ -335,8 +342,9 @@ Simulation {
index: StatePartIndex<BigSlots>(3),
ty: UInt<4>,
},
maybe_changed: true,
state: 0x3,
last_state: 0x3,
last_state: 0x2,
},
],
trace_memories: {},
@ -349,9 +357,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: {},
..
}

View file

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

View file

@ -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: [
@ -137,6 +137,7 @@ Simulation {
index: StatePartIndex<BigSlots>(0),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x05,
last_state: 0x05,
},
@ -146,6 +147,7 @@ Simulation {
index: StatePartIndex<BigSlots>(2),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x06,
last_state: 0x06,
},
@ -160,7 +162,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: {},
..
}

View file

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

View file

@ -0,0 +1,749 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Enum {
A,
B,
C,
},
},
],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 33,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::which_in",
ty: UInt<8>,
},
SlotDebugData {
name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::data_in",
ty: UInt<8>,
},
SlotDebugData {
name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::which_out",
ty: UInt<8>,
},
SlotDebugData {
name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::data_out",
ty: UInt<8>,
},
SlotDebugData {
name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::enum_out",
ty: Enum {
A(UInt<8>),
B(UInt<8>),
C(UInt<8>),
},
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: ".0",
ty: UInt<2>,
},
SlotDebugData {
name: ".1",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<2>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: Enum {
A(UInt<8>),
B(UInt<8>),
C(UInt<8>),
},
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: Bool,
},
SlotDebugData {
name: ".0",
ty: UInt<2>,
},
SlotDebugData {
name: ".1",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<2>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: Enum {
A(UInt<8>),
B(UInt<8>),
C(UInt<8>),
},
},
SlotDebugData {
name: ".0",
ty: UInt<2>,
},
SlotDebugData {
name: ".1",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<2>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: UInt<10>,
},
SlotDebugData {
name: "",
ty: Enum {
A(UInt<8>),
B(UInt<8>),
C(UInt<8>),
},
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
],
..
},
sim_only_slots: StatePartLayout<SimOnlySlots> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(32), // (0x2) SlotDebugData { name: "", ty: UInt<8> },
value: 0x2,
},
1: Const {
dest: StatePartIndex<BigSlots>(27), // (0x2) SlotDebugData { name: "", ty: UInt<2> },
value: 0x2,
},
2: Copy {
dest: StatePartIndex<BigSlots>(25), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> },
src: StatePartIndex<BigSlots>(27), // (0x2) SlotDebugData { name: "", ty: UInt<2> },
},
3: Copy {
dest: StatePartIndex<BigSlots>(26), // (0xe1) SlotDebugData { name: ".1", ty: UInt<8> },
src: StatePartIndex<BigSlots>(1), // (0xe1) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::data_in", ty: UInt<8> },
},
4: Shl {
dest: StatePartIndex<BigSlots>(28), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
lhs: StatePartIndex<BigSlots>(26), // (0xe1) SlotDebugData { name: ".1", ty: UInt<8> },
rhs: 2,
},
5: Or {
dest: StatePartIndex<BigSlots>(29), // (0x386) SlotDebugData { name: "", ty: UInt<10> },
lhs: StatePartIndex<BigSlots>(25), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> },
rhs: StatePartIndex<BigSlots>(28), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
},
6: CastToUInt {
dest: StatePartIndex<BigSlots>(30), // (0x386) SlotDebugData { name: "", ty: UInt<10> },
src: StatePartIndex<BigSlots>(29), // (0x386) SlotDebugData { name: "", ty: UInt<10> },
dest_width: 10,
},
7: Copy {
dest: StatePartIndex<BigSlots>(31), // (0x386) SlotDebugData { name: "", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
src: StatePartIndex<BigSlots>(30), // (0x386) SlotDebugData { name: "", ty: UInt<10> },
},
8: Const {
dest: StatePartIndex<BigSlots>(20), // (0x1) SlotDebugData { name: "", ty: UInt<2> },
value: 0x1,
},
9: Copy {
dest: StatePartIndex<BigSlots>(18), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> },
src: StatePartIndex<BigSlots>(20), // (0x1) SlotDebugData { name: "", ty: UInt<2> },
},
10: Copy {
dest: StatePartIndex<BigSlots>(19), // (0xe1) SlotDebugData { name: ".1", ty: UInt<8> },
src: StatePartIndex<BigSlots>(1), // (0xe1) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::data_in", ty: UInt<8> },
},
11: Shl {
dest: StatePartIndex<BigSlots>(21), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
lhs: StatePartIndex<BigSlots>(19), // (0xe1) SlotDebugData { name: ".1", ty: UInt<8> },
rhs: 2,
},
12: Or {
dest: StatePartIndex<BigSlots>(22), // (0x385) SlotDebugData { name: "", ty: UInt<10> },
lhs: StatePartIndex<BigSlots>(18), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> },
rhs: StatePartIndex<BigSlots>(21), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
},
13: CastToUInt {
dest: StatePartIndex<BigSlots>(23), // (0x385) SlotDebugData { name: "", ty: UInt<10> },
src: StatePartIndex<BigSlots>(22), // (0x385) SlotDebugData { name: "", ty: UInt<10> },
dest_width: 10,
},
14: Copy {
dest: StatePartIndex<BigSlots>(24), // (0x385) SlotDebugData { name: "", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
src: StatePartIndex<BigSlots>(23), // (0x385) SlotDebugData { name: "", ty: UInt<10> },
},
15: Const {
dest: StatePartIndex<BigSlots>(16), // (0x1) SlotDebugData { name: "", ty: UInt<8> },
value: 0x1,
},
16: CmpEq {
dest: StatePartIndex<BigSlots>(17), // (0x0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<BigSlots>(0), // (0x2) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::which_in", ty: UInt<8> },
rhs: StatePartIndex<BigSlots>(16), // (0x1) SlotDebugData { name: "", ty: UInt<8> },
},
17: Const {
dest: StatePartIndex<BigSlots>(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> },
value: 0x0,
},
18: Copy {
dest: StatePartIndex<BigSlots>(9), // (0x0) SlotDebugData { name: ".0", ty: UInt<2> },
src: StatePartIndex<BigSlots>(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> },
},
19: Copy {
dest: StatePartIndex<BigSlots>(10), // (0xe1) SlotDebugData { name: ".1", ty: UInt<8> },
src: StatePartIndex<BigSlots>(1), // (0xe1) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::data_in", ty: UInt<8> },
},
20: Shl {
dest: StatePartIndex<BigSlots>(12), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
lhs: StatePartIndex<BigSlots>(10), // (0xe1) SlotDebugData { name: ".1", ty: UInt<8> },
rhs: 2,
},
21: Or {
dest: StatePartIndex<BigSlots>(13), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
lhs: StatePartIndex<BigSlots>(9), // (0x0) SlotDebugData { name: ".0", ty: UInt<2> },
rhs: StatePartIndex<BigSlots>(12), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
},
22: CastToUInt {
dest: StatePartIndex<BigSlots>(14), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
src: StatePartIndex<BigSlots>(13), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
dest_width: 10,
},
23: Copy {
dest: StatePartIndex<BigSlots>(15), // (0x384) SlotDebugData { name: "", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
src: StatePartIndex<BigSlots>(14), // (0x384) SlotDebugData { name: "", ty: UInt<10> },
},
24: Const {
dest: StatePartIndex<BigSlots>(7), // (0x0) SlotDebugData { name: "", ty: UInt<8> },
value: 0x0,
},
25: CmpEq {
dest: StatePartIndex<BigSlots>(8), // (0x0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<BigSlots>(0), // (0x2) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::which_in", ty: UInt<8> },
rhs: StatePartIndex<BigSlots>(7), // (0x0) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:7:1
26: BranchIfZero {
target: 28,
value: StatePartIndex<BigSlots>(8), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:8:1
27: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x386) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::enum_out", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
src: StatePartIndex<BigSlots>(15), // (0x384) SlotDebugData { name: "", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
},
// at: module-XXXXXXXXXX.rs:7:1
28: BranchIfNonZero {
target: 33,
value: StatePartIndex<BigSlots>(8), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:9:1
29: BranchIfZero {
target: 31,
value: StatePartIndex<BigSlots>(17), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:10:1
30: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x386) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::enum_out", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
src: StatePartIndex<BigSlots>(24), // (0x385) SlotDebugData { name: "", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
},
// at: module-XXXXXXXXXX.rs:9:1
31: BranchIfNonZero {
target: 33,
value: StatePartIndex<BigSlots>(17), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:11:1
32: Copy {
dest: StatePartIndex<BigSlots>(4), // (0x386) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::enum_out", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
src: StatePartIndex<BigSlots>(31), // (0x386) SlotDebugData { name: "", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
},
// at: module-XXXXXXXXXX.rs:1:1
33: Copy {
dest: StatePartIndex<BigSlots>(5), // (0x386) SlotDebugData { name: "", ty: UInt<10> },
src: StatePartIndex<BigSlots>(4), // (0x386) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::enum_out", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
},
34: SliceInt {
dest: StatePartIndex<BigSlots>(6), // (0xe1) SlotDebugData { name: "", ty: UInt<8> },
src: StatePartIndex<BigSlots>(5), // (0x386) SlotDebugData { name: "", ty: UInt<10> },
start: 2,
len: 8,
},
// at: module-XXXXXXXXXX.rs:6:1
35: AndBigWithSmallImmediate {
dest: StatePartIndex<SmallSlots>(0), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} },
lhs: StatePartIndex<BigSlots>(4), // (0x386) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::enum_out", ty: Enum {A(UInt<8>), B(UInt<8>), C(UInt<8>)} },
rhs: 0x3,
},
// at: module-XXXXXXXXXX.rs:12:1
36: BranchIfSmallNeImmediate {
target: 39,
lhs: StatePartIndex<SmallSlots>(0), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} },
rhs: 0x0,
},
// at: module-XXXXXXXXXX.rs:13:1
37: Copy {
dest: StatePartIndex<BigSlots>(2), // (0x2) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::which_out", ty: UInt<8> },
src: StatePartIndex<BigSlots>(7), // (0x0) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:14:1
38: Copy {
dest: StatePartIndex<BigSlots>(3), // (0xe1) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::data_out", ty: UInt<8> },
src: StatePartIndex<BigSlots>(6), // (0xe1) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:12:1
39: BranchIfSmallNeImmediate {
target: 42,
lhs: StatePartIndex<SmallSlots>(0), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:15:1
40: Copy {
dest: StatePartIndex<BigSlots>(2), // (0x2) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::which_out", ty: UInt<8> },
src: StatePartIndex<BigSlots>(16), // (0x1) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:16:1
41: Copy {
dest: StatePartIndex<BigSlots>(3), // (0xe1) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::data_out", ty: UInt<8> },
src: StatePartIndex<BigSlots>(6), // (0xe1) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:12:1
42: BranchIfSmallNeImmediate {
target: 45,
lhs: StatePartIndex<SmallSlots>(0), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} },
rhs: 0x2,
},
// at: module-XXXXXXXXXX.rs:17:1
43: Copy {
dest: StatePartIndex<BigSlots>(2), // (0x2) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::which_out", ty: UInt<8> },
src: StatePartIndex<BigSlots>(32), // (0x2) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:18:1
44: Copy {
dest: StatePartIndex<BigSlots>(3), // (0xe1) SlotDebugData { name: "InstantiatedModule(enum_with_simple_body: enum_with_simple_body).enum_with_simple_body::data_out", ty: UInt<8> },
src: StatePartIndex<BigSlots>(6), // (0xe1) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:1:1
45: Return,
],
..
},
pc: 45,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [
2,
],
},
big_slots: StatePart {
value: [
2,
225,
2 (modified),
225 (modified),
902,
902,
225,
0,
0,
0,
225,
0,
900,
900,
900,
900,
1,
0,
1,
225,
1,
900,
901,
901,
901,
2,
225,
2,
900,
902,
902,
902,
2,
],
},
sim_only_slots: StatePart {
value: [],
},
},
io: Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
},
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.which_in,
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.data_in,
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.which_out,
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.data_out,
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.enum_out,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.data_in,
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.data_out,
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.enum_out,
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.which_in,
Instance {
name: <simulator>::enum_with_simple_body,
instantiated: Module {
name: enum_with_simple_body,
..
},
}.which_out,
},
did_initial_settle: true,
clocks_for_past: {},
},
extern_modules: [],
trace_decls: TraceModule {
name: "enum_with_simple_body",
children: [
TraceModuleIO {
name: "which_in",
child: TraceUInt {
location: TraceScalarId(0),
name: "which_in",
ty: UInt<8>,
flow: Source,
},
ty: UInt<8>,
flow: Source,
},
TraceModuleIO {
name: "data_in",
child: TraceUInt {
location: TraceScalarId(1),
name: "data_in",
ty: UInt<8>,
flow: Source,
},
ty: UInt<8>,
flow: Source,
},
TraceModuleIO {
name: "which_out",
child: TraceUInt {
location: TraceScalarId(2),
name: "which_out",
ty: UInt<8>,
flow: Sink,
},
ty: UInt<8>,
flow: Sink,
},
TraceModuleIO {
name: "data_out",
child: TraceUInt {
location: TraceScalarId(3),
name: "data_out",
ty: UInt<8>,
flow: Sink,
},
ty: UInt<8>,
flow: Sink,
},
TraceModuleIO {
name: "enum_out",
child: TraceEnumWithFields {
name: "enum_out",
discriminant: TraceEnumDiscriminant {
location: TraceScalarId(4),
name: "$tag",
ty: Enum {
A(UInt<8>),
B(UInt<8>),
C(UInt<8>),
},
flow: Sink,
},
non_empty_fields: [
TraceUInt {
location: TraceScalarId(5),
name: "A",
ty: UInt<8>,
flow: Source,
},
TraceUInt {
location: TraceScalarId(6),
name: "B",
ty: UInt<8>,
flow: Source,
},
TraceUInt {
location: TraceScalarId(7),
name: "C",
ty: UInt<8>,
flow: Source,
},
],
ty: Enum {
A(UInt<8>),
B(UInt<8>),
C(UInt<8>),
},
flow: Sink,
},
ty: Enum {
A(UInt<8>),
B(UInt<8>),
C(UInt<8>),
},
flow: Sink,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigUInt {
index: StatePartIndex<BigSlots>(0),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x02,
last_state: 0x02,
},
SimTrace {
id: TraceScalarId(1),
kind: BigUInt {
index: StatePartIndex<BigSlots>(1),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xe1,
last_state: 0xb4,
},
SimTrace {
id: TraceScalarId(2),
kind: BigUInt {
index: StatePartIndex<BigSlots>(2),
ty: UInt<8>,
},
maybe_changed: true,
state: 0x02,
last_state: 0x02,
},
SimTrace {
id: TraceScalarId(3),
kind: BigUInt {
index: StatePartIndex<BigSlots>(3),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xe1,
last_state: 0xb4,
},
SimTrace {
id: TraceScalarId(4),
kind: EnumDiscriminant {
index: StatePartIndex<SmallSlots>(0),
ty: Enum {
A(UInt<8>),
B(UInt<8>),
C(UInt<8>),
},
},
maybe_changed: true,
state: 0x2,
last_state: 0x2,
},
SimTrace {
id: TraceScalarId(5),
kind: BigUInt {
index: StatePartIndex<BigSlots>(6),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xe1,
last_state: 0xb4,
},
SimTrace {
id: TraceScalarId(6),
kind: BigUInt {
index: StatePartIndex<BigSlots>(6),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xe1,
last_state: 0xb4,
},
SimTrace {
id: TraceScalarId(7),
kind: BigUInt {
index: StatePartIndex<BigSlots>(6),
ty: UInt<8>,
},
maybe_changed: true,
state: 0xe1,
last_state: 0xb4,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
clocks_triggered: [],
event_queue: EventQueue(EventQueueData {
instant: 18 μs,
events: {},
}),
waiting_sensitivity_sets_by_address: {},
waiting_sensitivity_sets_by_compiled_value: {},
..
}

View file

@ -0,0 +1,133 @@
$timescale 1 ps $end
$scope module enum_with_simple_body $end
$var wire 8 J&-ne which_in $end
$var wire 8 \7mo/ data_in $end
$var wire 8 ,`>ir which_out $end
$var wire 8 0_gMP data_out $end
$scope struct enum_out $end
$var string 1 kFH/w \$tag $end
$var wire 8 |EI_= A $end
$var wire 8 !pRd4 B $end
$var wire 8 &RAbd C $end
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
b0 J&-ne
b0 \7mo/
b0 ,`>ir
b0 0_gMP
sA\x20(0) kFH/w
b0 |EI_=
b0 !pRd4
b0 &RAbd
$end
#1000000
b101101 \7mo/
b101101 0_gMP
b101101 |EI_=
b101101 !pRd4
b101101 &RAbd
#2000000
b1011010 \7mo/
b1011010 0_gMP
b1011010 |EI_=
b1011010 !pRd4
b1011010 &RAbd
#3000000
b10000111 \7mo/
b10000111 0_gMP
b10000111 |EI_=
b10000111 !pRd4
b10000111 &RAbd
#4000000
b10110100 \7mo/
b10110100 0_gMP
b10110100 |EI_=
b10110100 !pRd4
b10110100 &RAbd
#5000000
b11100001 \7mo/
b11100001 0_gMP
b11100001 |EI_=
b11100001 !pRd4
b11100001 &RAbd
#6000000
b1 J&-ne
b0 \7mo/
b1 ,`>ir
b0 0_gMP
sB\x20(1) kFH/w
b0 |EI_=
b0 !pRd4
b0 &RAbd
#7000000
b101101 \7mo/
b101101 0_gMP
b101101 |EI_=
b101101 !pRd4
b101101 &RAbd
#8000000
b1011010 \7mo/
b1011010 0_gMP
b1011010 |EI_=
b1011010 !pRd4
b1011010 &RAbd
#9000000
b10000111 \7mo/
b10000111 0_gMP
b10000111 |EI_=
b10000111 !pRd4
b10000111 &RAbd
#10000000
b10110100 \7mo/
b10110100 0_gMP
b10110100 |EI_=
b10110100 !pRd4
b10110100 &RAbd
#11000000
b11100001 \7mo/
b11100001 0_gMP
b11100001 |EI_=
b11100001 !pRd4
b11100001 &RAbd
#12000000
b10 J&-ne
b0 \7mo/
b10 ,`>ir
b0 0_gMP
sC\x20(2) kFH/w
b0 |EI_=
b0 !pRd4
b0 &RAbd
#13000000
b101101 \7mo/
b101101 0_gMP
b101101 |EI_=
b101101 !pRd4
b101101 &RAbd
#14000000
b1011010 \7mo/
b1011010 0_gMP
b1011010 |EI_=
b1011010 !pRd4
b1011010 &RAbd
#15000000
b10000111 \7mo/
b10000111 0_gMP
b10000111 |EI_=
b10000111 !pRd4
b10000111 &RAbd
#16000000
b10110100 \7mo/
b10110100 0_gMP
b10110100 |EI_=
b10110100 !pRd4
b10110100 &RAbd
#17000000
b11100001 \7mo/
b11100001 0_gMP
b11100001 |EI_=
b11100001 !pRd4
b11100001 &RAbd
#18000000

View file

@ -1003,183 +1003,186 @@ 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 },
},
// at: module-XXXXXXXXXX.rs:1:1
97: Const {
dest: StatePartIndex<BigSlots>(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> },
value: 0x0,
},
98: 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,
value: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::en", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:13:1
100: BranchIfZero {
target: 102,
value: StatePartIndex<BigSlots>(46), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:14:1
101: 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,
value: StatePartIndex<BigSlots>(46), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:15:1
103: BranchIfZero {
target: 105,
value: StatePartIndex<BigSlots>(48), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:16:1
104: 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,
value: StatePartIndex<BigSlots>(48), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:17:1
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>(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 {
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 },
},
108: AndSmall {
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:10:1
99: 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>(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
},
// at: module-XXXXXXXXXX.rs:1:1
100: Const {
dest: StatePartIndex<BigSlots>(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> },
value: 0x0,
},
101: 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
102: BranchIfZero {
target: 110,
value: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::en", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:13:1
103: BranchIfZero {
target: 105,
value: StatePartIndex<BigSlots>(46), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:14:1
104: 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
105: BranchIfNonZero {
target: 110,
value: StatePartIndex<BigSlots>(46), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:15:1
106: BranchIfZero {
target: 108,
value: StatePartIndex<BigSlots>(48), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:16:1
107: 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
108: BranchIfNonZero {
target: 110,
value: StatePartIndex<BigSlots>(48), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:17:1
109: 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:10:1
110: Copy {
dest: StatePartIndex<BigSlots>(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} },
src: StatePartIndex<BigSlots>(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} },
},
// at: module-XXXXXXXXXX.rs:1:1
110: Copy {
111: Copy {
dest: StatePartIndex<BigSlots>(18), // (0x0) SlotDebugData { name: "", ty: UInt<3> },
src: StatePartIndex<BigSlots>(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} },
},
111: SliceInt {
112: SliceInt {
dest: StatePartIndex<BigSlots>(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> },
src: StatePartIndex<BigSlots>(18), // (0x0) SlotDebugData { name: "", ty: UInt<3> },
start: 1,
len: 2,
},
112: SliceInt {
113: SliceInt {
dest: StatePartIndex<BigSlots>(20), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
src: StatePartIndex<BigSlots>(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> },
start: 0,
len: 1,
},
113: SliceInt {
114: SliceInt {
dest: StatePartIndex<BigSlots>(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
src: StatePartIndex<BigSlots>(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> },
start: 1,
len: 1,
},
114: Copy {
115: Copy {
dest: StatePartIndex<BigSlots>(22), // (0x0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
},
115: Copy {
116: Copy {
dest: StatePartIndex<BigSlots>(16), // (0x0) SlotDebugData { name: ".0", ty: UInt<1> },
src: StatePartIndex<BigSlots>(20), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
},
116: Copy {
117: Copy {
dest: StatePartIndex<BigSlots>(17), // (0x0) SlotDebugData { name: ".1", ty: Bool },
src: StatePartIndex<BigSlots>(22), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:9:1
117: AndBigWithSmallImmediate {
118: AndBigWithSmallImmediate {
dest: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome} },
lhs: StatePartIndex<BigSlots>(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:1:1
118: Copy {
119: Copy {
dest: StatePartIndex<BigSlots>(10), // (0x0) SlotDebugData { name: "", ty: UInt<3> },
src: StatePartIndex<BigSlots>(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} },
},
119: SliceInt {
120: SliceInt {
dest: StatePartIndex<BigSlots>(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> },
src: StatePartIndex<BigSlots>(10), // (0x0) SlotDebugData { name: "", ty: UInt<3> },
start: 1,
len: 2,
},
120: SliceInt {
121: SliceInt {
dest: StatePartIndex<BigSlots>(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
src: StatePartIndex<BigSlots>(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> },
start: 0,
len: 1,
},
121: SliceInt {
122: SliceInt {
dest: StatePartIndex<BigSlots>(13), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
src: StatePartIndex<BigSlots>(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> },
start: 1,
len: 1,
},
122: Copy {
123: Copy {
dest: StatePartIndex<BigSlots>(14), // (0x0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(13), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
},
123: Copy {
124: Copy {
dest: StatePartIndex<BigSlots>(8), // (0x0) SlotDebugData { name: ".0", ty: UInt<1> },
src: StatePartIndex<BigSlots>(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
},
124: Copy {
125: Copy {
dest: StatePartIndex<BigSlots>(9), // (0x0) SlotDebugData { name: ".1", ty: Bool },
src: StatePartIndex<BigSlots>(14), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:8:1
125: AndBigWithSmallImmediate {
126: AndBigWithSmallImmediate {
dest: StatePartIndex<SmallSlots>(0), // (0x0 0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome} },
lhs: StatePartIndex<BigSlots>(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:11:1
126: BranchIfSmallZero {
target: 131,
127: BranchIfSmallZero {
target: 132,
value: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
127: BranchIfSmallNonZero {
target: 130,
128: BranchIfSmallNonZero {
target: 131,
value: StatePartIndex<SmallSlots>(5), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
128: Copy {
129: Copy {
dest: StatePartIndex<BigSlots>(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>})} },
src: 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>})} },
},
129: Branch {
target: 131,
130: Branch {
target: 132,
},
130: Copy {
131: Copy {
dest: StatePartIndex<BigSlots>(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", 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>})} },
},
131: XorSmallImmediate {
132: XorSmallImmediate {
dest: StatePartIndex<SmallSlots>(2), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(4), // (0x1 1) SlotDebugData { name: "", ty: Bool },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:1:1
132: Return,
133: Return,
],
..
},
pc: 132,
pc: 133,
memory_write_log: [],
memories: StatePart {
value: [],
@ -1454,9 +1457,9 @@ Simulation {
}.which_out,
},
did_initial_settle: true,
clocks_for_past: {},
},
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "enums",
children: [
@ -1743,14 +1746,16 @@ Simulation {
kind: BigClock {
index: StatePartIndex<BigSlots>(0),
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
last_state: 0x0,
},
SimTrace {
id: TraceScalarId(1),
kind: BigSyncReset {
index: StatePartIndex<BigSlots>(1),
},
maybe_changed: false,
state: 0x0,
last_state: 0x0,
},
@ -1759,6 +1764,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(2),
},
maybe_changed: false,
state: 0x1,
last_state: 0x1,
},
@ -1768,6 +1774,7 @@ Simulation {
index: StatePartIndex<BigSlots>(3),
ty: UInt<2>,
},
maybe_changed: false,
state: 0x2,
last_state: 0x2,
},
@ -1777,6 +1784,7 @@ Simulation {
index: StatePartIndex<BigSlots>(4),
ty: UInt<4>,
},
maybe_changed: false,
state: 0xf,
last_state: 0xf,
},
@ -1786,6 +1794,7 @@ Simulation {
index: StatePartIndex<BigSlots>(5),
ty: UInt<2>,
},
maybe_changed: true,
state: 0x2,
last_state: 0x2,
},
@ -1795,6 +1804,7 @@ Simulation {
index: StatePartIndex<BigSlots>(6),
ty: UInt<4>,
},
maybe_changed: true,
state: 0xf,
last_state: 0xf,
},
@ -1807,6 +1817,7 @@ Simulation {
HdlSome(Bundle {0: UInt<1>, 1: Bool}),
},
},
maybe_changed: true,
state: 0x0,
last_state: 0x0,
},
@ -1816,6 +1827,7 @@ Simulation {
index: StatePartIndex<BigSlots>(8),
ty: UInt<1>,
},
maybe_changed: true,
state: 0x0,
last_state: 0x0,
},
@ -1824,6 +1836,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(9),
},
maybe_changed: true,
state: 0x0,
last_state: 0x0,
},
@ -1836,6 +1849,7 @@ Simulation {
HdlSome(Bundle {0: UInt<1>, 1: Bool}),
},
},
maybe_changed: true,
state: 0x0,
last_state: 0x0,
},
@ -1845,6 +1859,7 @@ Simulation {
index: StatePartIndex<BigSlots>(16),
ty: UInt<1>,
},
maybe_changed: true,
state: 0x0,
last_state: 0x0,
},
@ -1853,6 +1868,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(17),
},
maybe_changed: true,
state: 0x0,
last_state: 0x0,
},
@ -1866,6 +1882,7 @@ Simulation {
C(Bundle {a: Array<UInt<1>, 2>, b: SInt<2>}),
},
},
maybe_changed: true,
state: 0x2,
last_state: 0x2,
},
@ -1875,6 +1892,7 @@ Simulation {
index: StatePartIndex<BigSlots>(27),
ty: UInt<1>,
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
},
@ -1883,6 +1901,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(28),
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
},
@ -1892,6 +1911,7 @@ Simulation {
index: StatePartIndex<BigSlots>(34),
ty: UInt<1>,
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
},
@ -1901,6 +1921,7 @@ Simulation {
index: StatePartIndex<BigSlots>(35),
ty: UInt<1>,
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
},
@ -1910,6 +1931,7 @@ Simulation {
index: StatePartIndex<BigSlots>(36),
ty: SInt<2>,
},
maybe_changed: true,
state: 0x3,
last_state: 0x3,
},
@ -1924,9 +1946,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: {},
..
}

View file

@ -1,126 +1,126 @@
$timescale 1 ps $end
$scope module enums $end
$scope struct cd $end
$var wire 1 ! clk $end
$var wire 1 " rst $end
$var wire 1 0n\U< clk $end
$var wire 1 a?A!) rst $end
$upscope $end
$var wire 1 # en $end
$var wire 2 $ which_in $end
$var wire 4 % data_in $end
$var wire 2 & which_out $end
$var wire 4 ' data_out $end
$var wire 1 #ZQY# en $end
$var wire 2 8?II+ which_in $end
$var wire 4 OO,N+ data_in $end
$var wire 2 yr2gr which_out $end
$var wire 4 q_O;Y data_out $end
$scope struct b_out $end
$var string 1 ( \$tag $end
$var string 1 7L1gf \$tag $end
$scope struct HdlSome $end
$var wire 1 ) \0 $end
$var wire 1 * \1 $end
$var wire 1 EO?Ju \0 $end
$var wire 1 cGtNN \1 $end
$upscope $end
$upscope $end
$scope struct b2_out $end
$var string 1 + \$tag $end
$var string 1 dqd@B \$tag $end
$scope struct HdlSome $end
$var wire 1 , \0 $end
$var wire 1 - \1 $end
$var wire 1 (FG:I \0 $end
$var wire 1 dzy-= \1 $end
$upscope $end
$upscope $end
$scope struct the_reg $end
$var string 1 . \$tag $end
$var string 1 J#9uO \$tag $end
$scope struct B $end
$var reg 1 / \0 $end
$var reg 1 0 \1 $end
$var reg 1 ca2Gh \0 $end
$var reg 1 f)r)? \1 $end
$upscope $end
$scope struct C $end
$scope struct a $end
$var reg 1 1 \[0] $end
$var reg 1 2 \[1] $end
$var reg 1 ;BepJ \[0] $end
$var reg 1 J~2;e \[1] $end
$upscope $end
$var reg 2 3 b $end
$var reg 2 w\b)K b $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
0!
1"
0#
b0 $
b0 %
b0 &
b0 '
sHdlNone\x20(0) (
0)
0*
sHdlNone\x20(0) +
0,
0-
sA\x20(0) .
0/
00
01
02
b0 3
00n\U<
1a?A!)
0#ZQY#
b0 8?II+
b0 OO,N+
b0 yr2gr
b0 q_O;Y
sHdlNone\x20(0) 7L1gf
0EO?Ju
0cGtNN
sHdlNone\x20(0) dqd@B
0(FG:I
0dzy-=
sA\x20(0) J#9uO
0ca2Gh
0f)r)?
0;BepJ
0J~2;e
b0 w\b)K
$end
#1000000
1!
10n\U<
#1100000
0"
0a?A!)
#2000000
0!
00n\U<
#3000000
1!
10n\U<
#4000000
1#
b1 $
0!
1#ZQY#
b1 8?II+
00n\U<
#5000000
1!
b1 &
sHdlSome\x20(1) (
sHdlSome\x20(1) +
sB\x20(1) .
10n\U<
b1 yr2gr
sHdlSome\x20(1) 7L1gf
sHdlSome\x20(1) dqd@B
sB\x20(1) J#9uO
#6000000
0#
b0 $
0!
0#ZQY#
b0 8?II+
00n\U<
#7000000
1!
10n\U<
#8000000
1#
b1 $
b1111 %
0!
1#ZQY#
b1 8?II+
b1111 OO,N+
00n\U<
#9000000
1!
b11 '
1)
1*
1,
1-
1/
10
11
12
10n\U<
b11 q_O;Y
1EO?Ju
1cGtNN
1(FG:I
1dzy-=
1ca2Gh
1f)r)?
1;BepJ
1J~2;e
#10000000
0!
00n\U<
#11000000
1!
10n\U<
#12000000
b10 $
0!
b10 8?II+
00n\U<
#13000000
1!
b10 &
b1111 '
sHdlNone\x20(0) (
0)
0*
sHdlNone\x20(0) +
0,
0-
sC\x20(2) .
b11 3
10n\U<
b10 yr2gr
b1111 q_O;Y
sHdlNone\x20(0) 7L1gf
0EO?Ju
0cGtNN
sHdlNone\x20(0) dqd@B
0(FG:I
0dzy-=
sC\x20(2) J#9uO
b11 w\b)K
#14000000
0!
00n\U<
#15000000
1!
10n\U<
#16000000

View file

@ -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: [
@ -225,6 +221,7 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(0),
},
maybe_changed: false,
state: 0x1,
last_state: 0x1,
},
@ -233,8 +230,9 @@ Simulation {
kind: BigBool {
index: StatePartIndex<BigSlots>(1),
},
maybe_changed: true,
state: 0x1,
last_state: 0x1,
last_state: 0x0,
},
],
trace_memories: {},
@ -247,7 +245,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: {},
..
}

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