Compare commits

..

6 commits

Author SHA1 Message Date
Tobias Alexandra Platen
8433f4f150 increase memory bandwidth and size 2025-10-19 10:26:26 +02:00
Tobias Alexandra Platen
672a29e76d run cargo fmt on main_memory 2025-10-14 20:11:24 +02:00
Tobias Alexandra Platen
35ea85d074 add write port 2025-10-14 20:08:29 +02:00
Tobias Alexandra Platen
1ead550d13 use for loop in unit test 2025-10-14 18:50:29 +02:00
Tobias Alexandra Platen
540a91878c more work on test case 2025-10-05 12:11:27 +02:00
Tobias Alexandra Platen
9f68cbb953 add main_memory 2025-10-05 11:04:18 +02:00
50 changed files with 37132 additions and 551424 deletions

View file

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

View file

@ -3,18 +3,57 @@
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
deps:
runs-on: debian-12
uses: ./.forgejo/workflows/deps.yml
test: test:
runs-on: debian-12 runs-on: debian-12
container: needs: deps
image: git.libre-chip.org/libre-chip/fayalite-deps:latest
steps: steps:
- uses: actions/checkout@v3 - uses: https://git.libre-chip.org/mirrors/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- run: | - run: |
scripts/check-copyright.sh scripts/check-copyright.sh
- run: |
apt-get update -qq
apt-get install -qq \
bison \
build-essential \
ccache \
clang \
cvc5 \
flex \
gawk \
git \
libboost-filesystem-dev \
libboost-python-dev \
libboost-system-dev \
libffi-dev \
libreadline-dev \
lld \
pkg-config \
python3 \
python3-click \
tcl-dev \
z3 \
zlib1g-dev
- run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.89.0
source "$HOME/.cargo/env"
echo "$PATH" >> "$GITHUB_PATH"
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
with:
path: deps
key: ${{ needs.deps.outputs.cache-primary-key }}
fail-on-cache-miss: true
- run: |
make -C deps/z3/build install
make -C deps/sby install
make -C deps/yosys install
export PATH="$(realpath deps/firtool/bin):$PATH"
echo "$PATH" >> "$GITHUB_PATH"
- uses: https://git.libre-chip.org/mirrors/rust-cache@v2 - uses: https://git.libre-chip.org/mirrors/rust-cache@v2
with: with:
save-if: ${{ github.ref == 'refs/heads/master' }} save-if: ${{ github.ref == 'refs/heads/master' }}
- run: rustup override set 1.93.0
- run: cargo test - run: cargo test

3
.gitattributes vendored
View file

@ -1,3 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# See Notices.txt for copyright information
*.vcd linguist-generated=true

1
.gitignore vendored
View file

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

610
Cargo.lock generated
View file

@ -1,20 +1,17 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 3
[[package]] [[package]]
name = "adler2" name = "ahash"
version = "2.0.1" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [ dependencies = [
"memchr", "cfg-if",
"once_cell",
"version_check",
"zerocopy",
] ]
[[package]] [[package]]
@ -96,36 +93,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base16ct"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bindgen"
version = "0.71.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.6.0" version = "2.6.0"
@ -168,48 +135,21 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bytes"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.51" version = "1.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
dependencies = [ dependencies = [
"find-msvc-tools",
"shlex", "shlex",
] ]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.20" version = "4.5.20"
@ -232,15 +172,6 @@ dependencies = [
"strsim", "strsim",
] ]
[[package]]
name = "clap_complete"
version = "4.5.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2348487adcd4631696ced64ccdb40d38ac4d31cae7f2eec8817fcea1b9d1c43c"
dependencies = [
"clap",
]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.18" version = "4.5.18"
@ -275,17 +206,7 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
name = "cpu" name = "cpu"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base16ct 1.0.0",
"fayalite", "fayalite",
"hex-literal",
"parse_powerisa_pdf",
"regex",
"roxmltree",
"serde",
"sha2",
"simple-mermaid",
"ureq",
"which",
] ]
[[package]] [[package]]
@ -297,15 +218,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -361,12 +273,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.14" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -388,23 +300,20 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]] [[package]]
name = "fayalite" name = "fayalite"
version = "0.3.0" version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c632e5d570d4763e8e18d764e95b7a9e515ebf99" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e"
dependencies = [ dependencies = [
"base64",
"bitvec", "bitvec",
"blake3", "blake3",
"clap", "clap",
"clap_complete",
"ctor", "ctor",
"eyre", "eyre",
"fayalite-proc-macros", "fayalite-proc-macros",
"fayalite-visit-gen", "fayalite-visit-gen",
"hashbrown 0.15.5", "hashbrown",
"jobslot", "jobslot",
"num-bigint", "num-bigint",
"num-traits", "num-traits",
"once_cell", "os_pipe",
"ordered-float",
"petgraph", "petgraph",
"serde", "serde",
"serde_json", "serde_json",
@ -416,7 +325,7 @@ dependencies = [
[[package]] [[package]]
name = "fayalite-proc-macros" name = "fayalite-proc-macros"
version = "0.3.0" version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c632e5d570d4763e8e18d764e95b7a9e515ebf99" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e"
dependencies = [ dependencies = [
"fayalite-proc-macros-impl", "fayalite-proc-macros-impl",
] ]
@ -424,9 +333,9 @@ dependencies = [
[[package]] [[package]]
name = "fayalite-proc-macros-impl" name = "fayalite-proc-macros-impl"
version = "0.3.0" version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c632e5d570d4763e8e18d764e95b7a9e515ebf99" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e"
dependencies = [ dependencies = [
"base16ct 0.2.0", "base16ct",
"num-bigint", "num-bigint",
"prettyplease", "prettyplease",
"proc-macro2", "proc-macro2",
@ -439,7 +348,7 @@ dependencies = [
[[package]] [[package]]
name = "fayalite-visit-gen" name = "fayalite-visit-gen"
version = "0.3.0" version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c632e5d570d4763e8e18d764e95b7a9e515ebf99" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"prettyplease", "prettyplease",
@ -451,34 +360,12 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "find-msvc-tools"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
[[package]] [[package]]
name = "fixedbitset" name = "fixedbitset"
version = "0.5.7" version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flate2"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]] [[package]]
name = "funty" name = "funty"
version = "2.0.0" version = "2.0.0"
@ -497,62 +384,31 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.16" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi",
] ]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.5" version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [ dependencies = [
"ahash",
"allocator-api2", "allocator-api2",
"equivalent",
"foldhash",
] ]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hex-literal"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1"
[[package]] [[package]]
name = "home" name = "home"
version = "0.5.9" version = "0.5.9"
@ -562,22 +418,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]] [[package]]
name = "indenter" name = "indenter"
version = "0.3.3" version = "0.3.3"
@ -586,14 +426,13 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.13.0" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.16.1", "hashbrown",
"serde", "serde",
"serde_core",
] ]
[[package]] [[package]]
@ -602,15 +441,6 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.11" version = "1.0.11"
@ -619,39 +449,23 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "jobslot" name = "jobslot"
version = "0.2.23" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c" checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"derive_destructure2", "derive_destructure2",
"getrandom 0.3.4", "getrandom",
"libc", "libc",
"scopeguard", "scopeguard",
"windows-sys 0.61.2", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.180" version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "libloading"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link",
]
[[package]]
name = "libm"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
@ -659,57 +473,12 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "mupdf-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e9a0d4e844ab50315d43312f3d62f72c77205b07c8ee21cbd4b52bdc2a9910"
dependencies = [
"bindgen",
"cc",
"pkg-config",
"regex",
"zerocopy",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.6" version = "0.4.6"
@ -740,56 +509,29 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]] [[package]]
name = "ordered-float" name = "os_pipe"
version = "5.1.0" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [ dependencies = [
"num-traits", "libc",
"rand", "windows-sys 0.59.0",
"serde",
] ]
[[package]]
name = "parse_powerisa_pdf"
version = "0.1.0"
source = "git+https://git.libre-chip.org/libre-chip/parse_powerisa_pdf.git?branch=master#38a1fb328bd44f26389c28fbf66716154f4113dc"
dependencies = [
"indexmap",
"libm",
"mupdf-sys",
"quick-xml",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]] [[package]]
name = "petgraph" name = "petgraph"
version = "0.8.3" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"hashbrown 0.15.5",
"indexmap", "indexmap",
"serde",
] ]
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.22" version = "0.2.22"
@ -809,15 +551,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "quick-xml"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.37" version = "1.0.37"
@ -827,95 +560,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]] [[package]]
name = "radium" name = "radium"
version = "0.7.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
"serde",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"serde",
]
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.16",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "roxmltree"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb"
dependencies = [
"memchr",
]
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.37" version = "0.38.37"
@ -929,41 +579,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rustls"
version = "0.23.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.18" version = "1.0.18"
@ -978,28 +593,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.228" version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1021,9 +626,9 @@ dependencies = [
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.9" version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
@ -1036,30 +641,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
name = "simple-mermaid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589144a964b4b30fe3a83b4bb1a09e2475aac194ec832a046a23e75bddf9eb29"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.96" version = "2.0.96"
@ -1122,47 +709,6 @@ version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a"
dependencies = [
"base64",
"flate2",
"log",
"percent-encoding",
"rustls",
"rustls-pki-types",
"ureq-proto",
"utf-8",
"webpki-roots",
]
[[package]]
name = "ureq-proto"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
dependencies = [
"base64",
"http",
"httparse",
"log",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@ -1183,27 +729,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.1+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[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 = "webpki-roots"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "which" name = "which"
@ -1213,17 +741,10 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
dependencies = [ dependencies = [
"either", "either",
"home", "home",
"regex",
"rustix", "rustix",
"winsafe", "winsafe",
] ]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -1242,15 +763,6 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.52.6"
@ -1321,12 +833,6 @@ version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]] [[package]]
name = "wyz" name = "wyz"
version = "0.5.1" version = "0.5.1"
@ -1338,26 +844,20 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.27" version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.27" version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"

View file

@ -11,19 +11,10 @@ edition = "2024"
repository = "" repository = ""
keywords = [] keywords = []
categories = [] categories = []
rust-version = "1.93.0" rust-version = "1.89.0"
[workspace.dependencies] [workspace.dependencies]
base16ct = "1.0.0"
fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" } fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" }
hex-literal = "1.1.0"
parse_powerisa_pdf = { git = "https://git.libre-chip.org/libre-chip/parse_powerisa_pdf.git", version = "0.1.0", branch = "master" }
roxmltree = "0.21.1"
serde = { version = "1.0.202", features = ["derive"] }
sha2 = "0.10.9"
simple-mermaid = "0.2.0"
ureq = "3.1.4"
which = { version = "6.0.3", features = ["regex"] }
[profile.dev] [profile.dev]
opt-level = 1 opt-level = 1

View file

@ -1,15 +0,0 @@
<!--
SPDX-License-Identifier: LGPL-3.0-or-later
See Notices.txt for copyright information
-->
# Libre-Chip's CPU
<https://libre-chip.org/first_arch/index.html>
# Funding
## NLnet Grants
* [Libre-Chip CPU with proof of No Spectre bugs](https://nlnet.nl/project/Libre-Chip-proof/) 2024-12-324 [(progress)](https://git.libre-chip.org/libre-chip/grant-tracking/src/branch/master/nlnet-2024-12-324/progress.md)
This project was funded through the [NGI0 Commons Fund](https://nlnet.nl/commonsfund), a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) programme, under the aegis of [DG Communications Networks, Content and Technology](https://commission.europa.eu/about-european-commission/departments-and-executive-agencies/communications-networks-content-and-technology_en) under grant agreement &numero; [101135429](https://cordis.europa.eu/project/id/101135429). Additional funding is made available by the [Swiss State Secretariat for Education, Research and Innovation](https://www.sbfi.admin.ch/sbfi/en/home.html) (SERI).

View file

@ -16,20 +16,3 @@ version.workspace = true
[dependencies] [dependencies]
fayalite.workspace = true fayalite.workspace = true
roxmltree.workspace = true
serde.workspace = true
simple-mermaid.workspace = true
[build-dependencies]
base16ct.workspace = true
hex-literal.workspace = true
parse_powerisa_pdf.workspace = true
sha2.workspace = true
ureq.workspace = true
[dev-dependencies]
base16ct.workspace = true
hex-literal.workspace = true
regex = "1.12.2"
sha2.workspace = true
which.workspace = true

View file

@ -1 +0,0 @@
../../README.md

View file

@ -1,96 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use parse_powerisa_pdf::parse_powerisa_pdf_and_generate_xml;
use sha2::{Digest, Sha256};
use std::{
error::Error,
io::{ErrorKind, Read},
path::Path,
};
const EXPECTED_FILE_SIZE: u64 = 6425593;
const EXPECTED_FILE_HASH: &[u8; 32] =
&hex_literal::hex!("56372d23ece7e9e2c1b381a639443982a3e16e38109df1c141d655b779b61fdb");
fn verify_powerisa_file(file: impl std::io::Read) -> Result<Vec<u8>, Box<dyn Error>> {
let mut buf = Vec::with_capacity(usize::try_from(EXPECTED_FILE_SIZE)? + 1);
file.take(EXPECTED_FILE_SIZE + 1).read_to_end(&mut buf)?;
if buf.len() > EXPECTED_FILE_SIZE as usize {
Err(format!("file is bigger than the expected length {EXPECTED_FILE_SIZE}").into())
} else if buf.len() < EXPECTED_FILE_SIZE as usize {
Err(format!(
"file is smaller than the expected length {EXPECTED_FILE_SIZE}: actual length {}",
buf.len()
)
.into())
} else {
let hash = Sha256::digest(&buf);
let hash = &*hash;
if hash != EXPECTED_FILE_HASH {
Err(format!(
"file's SHA256 hash doesn't match the expected hash: expected: {:x} got: {:x}",
base16ct::HexDisplay(EXPECTED_FILE_HASH),
base16ct::HexDisplay(hash)
)
.into())
} else {
Ok(buf)
}
}
}
fn is_powerisa_pdf(path: impl AsRef<Path>) -> Result<bool, Box<dyn Error>> {
let path = path.as_ref();
let metadata = match std::fs::metadata(path) {
Err(e) if e.kind() == ErrorKind::NotFound => return Ok(false),
v => v?,
};
if !metadata.is_file() {
return Ok(false);
}
let file = std::fs::File::open(path)?;
verify_powerisa_file(file)?;
Ok(true)
}
fn out_dir() -> String {
std::env::var("OUT_DIR").expect("OUT_DIR env var is not set or invalid")
}
fn get_powerisa_pdf_name_and_dir<'a>(
out_dir: &'a str,
) -> Result<(&'static str, &'a Path), Box<dyn Error>> {
const FILE_NAME: &str = "OPF_PowerISA_v3.1C.pdf";
let out_dir = Path::new(out_dir);
if is_powerisa_pdf(FILE_NAME)? {
println!("cargo::rerun-if-changed={FILE_NAME}");
return Ok((FILE_NAME, Path::new(".")));
}
let full_path = out_dir.join(FILE_NAME);
let full_path = full_path
.into_os_string()
.into_string()
.expect("should be valid UTF-8");
if is_powerisa_pdf(&full_path)? {
println!("cargo::rerun-if-changed={full_path}");
return Ok((FILE_NAME, out_dir));
}
const URL: &str = "https://libre-chip.org/OPF_PowerISA_v3.1C.pdf";
println!("cargo::warning={FILE_NAME} not found locally, downloading from {URL}");
let buf = verify_powerisa_file(ureq::get(URL).call()?.into_body().into_reader())?;
std::fs::write(&full_path, buf)?;
println!("cargo::rerun-if-changed={full_path}");
Ok((FILE_NAME, out_dir))
}
fn main() -> Result<(), Box<dyn Error>> {
let out_dir = out_dir();
let (pdf_name, pdf_dir) = get_powerisa_pdf_name_and_dir(&out_dir)?;
let old_dir = std::env::current_dir()?;
std::env::set_current_dir(pdf_dir)?;
let xml = parse_powerisa_pdf_and_generate_xml(pdf_name, None, false)?;
std::env::set_current_dir(old_dir)?;
std::fs::write(Path::new(&out_dir).join("powerisa-instructions.xml"), xml)?;
Ok(())
}

View file

@ -8,10 +8,9 @@ use crate::{
}, },
}; };
use fayalite::prelude::*; use fayalite::prelude::*;
use serde::{Deserialize, Serialize};
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct UnitConfig { pub struct UnitConfig {
pub kind: UnitKind, pub kind: UnitKind,
@ -28,18 +27,12 @@ impl UnitConfig {
} }
} }
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct CpuConfig { pub struct CpuConfig {
pub units: Vec<UnitConfig>, pub units: Vec<UnitConfig>,
pub out_reg_num_width: usize, pub out_reg_num_width: usize,
pub fetch_width: NonZeroUsize, pub fetch_width: NonZeroUsize,
pub max_branches_per_fetch: NonZeroUsize,
pub max_fetches_in_flight: NonZeroUsize,
pub log2_fetch_width_in_bytes: u8,
pub log2_cache_line_size_in_bytes: u8,
pub log2_l1_i_cache_line_count: u8,
pub l1_i_cache_max_misses_in_flight: NonZeroUsize,
/// default value for [`UnitConfig::max_in_flight`] /// default value for [`UnitConfig::max_in_flight`]
pub default_unit_max_in_flight: NonZeroUsize, pub default_unit_max_in_flight: NonZeroUsize,
pub rob_size: NonZeroUsize, pub rob_size: NonZeroUsize,
@ -53,27 +46,6 @@ impl CpuConfig {
}; };
v v
}; };
pub const DEFAULT_MAX_BRANCHES_PER_FETCH: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(1) else {
unreachable!();
};
v
};
pub const DEFAULT_MAX_FETCHES_IN_FLIGHT: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(16) else {
unreachable!();
};
v
};
pub const DEFAULT_LOG2_FETCH_WIDTH_IN_BYTES: u8 = 3;
pub const DEFAULT_LOG2_CACHE_LINE_SIZE_IN_BYTES: u8 = 6;
pub const DEFAULT_LOG2_L1_I_CACHE_LINE_COUNT: u8 = 8;
pub const DEFAULT_L1_I_CACHE_MAX_MISSES_IN_FLIGHT: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(2) else {
unreachable!();
};
v
};
pub const DEFAULT_UNIT_MAX_IN_FLIGHT: NonZeroUsize = { pub const DEFAULT_UNIT_MAX_IN_FLIGHT: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(8) else { let Some(v) = NonZeroUsize::new(8) else {
unreachable!(); unreachable!();
@ -85,12 +57,6 @@ impl CpuConfig {
units, units,
out_reg_num_width: Self::DEFAULT_OUT_REG_NUM_WIDTH, out_reg_num_width: Self::DEFAULT_OUT_REG_NUM_WIDTH,
fetch_width: Self::DEFAULT_FETCH_WIDTH, fetch_width: Self::DEFAULT_FETCH_WIDTH,
max_branches_per_fetch: Self::DEFAULT_MAX_BRANCHES_PER_FETCH,
max_fetches_in_flight: Self::DEFAULT_MAX_FETCHES_IN_FLIGHT,
log2_fetch_width_in_bytes: Self::DEFAULT_LOG2_FETCH_WIDTH_IN_BYTES,
log2_cache_line_size_in_bytes: Self::DEFAULT_LOG2_CACHE_LINE_SIZE_IN_BYTES,
log2_l1_i_cache_line_count: Self::DEFAULT_LOG2_L1_I_CACHE_LINE_COUNT,
l1_i_cache_max_misses_in_flight: Self::DEFAULT_L1_I_CACHE_MAX_MISSES_IN_FLIGHT,
default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT, default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT,
rob_size, rob_size,
} }
@ -150,100 +116,4 @@ impl CpuConfig {
UnitToRegAlloc[mop_ty][extra_out_ty][self.unit_num_width()][self.out_reg_num_width] UnitToRegAlloc[mop_ty][extra_out_ty][self.unit_num_width()][self.out_reg_num_width]
[self.non_const_unit_nums().len()] [self.non_const_unit_nums().len()]
} }
pub fn fetch_width_in_bytes(&self) -> usize {
1usize
.checked_shl(self.log2_fetch_width_in_bytes.into())
.expect("log2_fetch_width_in_bytes is too big")
}
pub fn cache_line_size_in_bytes(&self) -> usize {
1usize
.checked_shl(self.log2_cache_line_size_in_bytes.into())
.expect("log2_cache_line_size_in_bytes is too big")
}
pub fn log2_fetches_per_cache_line(&self) -> usize {
self.log2_cache_line_size_in_bytes
.checked_sub(self.log2_fetch_width_in_bytes)
.expect("cache line size in bytes must not be smaller than fetch width in bytes")
.into()
}
pub fn fetches_per_cache_line(&self) -> usize {
self.log2_fetches_per_cache_line()
.try_into()
.ok()
.and_then(|v| 1usize.checked_shl(v))
.expect("log2_fetches_per_cache_line is too big")
}
pub fn l1_i_cache_line_count(&self) -> usize {
1usize
.checked_shl(self.log2_l1_i_cache_line_count.into())
.expect("log2_l1_i_cache_line_count is too big")
}
pub fn log2_l1_i_cache_size_in_bytes(&self) -> usize {
self.log2_l1_i_cache_line_count as usize + self.log2_cache_line_size_in_bytes as usize
}
pub fn l1_i_cache_size_in_bytes(&self) -> usize {
1usize
.checked_shl(self.log2_l1_i_cache_size_in_bytes() as _)
.expect("L1 I-Cache is too big")
}
} }
#[hdl(get(|c| c.fetch_width.get()))]
pub type CpuConfigFetchWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.fetch_width.get() * 2))]
pub type TwiceCpuConfigFetchWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.max_branches_per_fetch.get()))]
pub type CpuConfigMaxBranchesPerFetch<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.max_fetches_in_flight.get()))]
pub type CpuConfigMaxFetchesInFlight<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.log2_fetch_width_in_bytes.into()))]
pub type CpuConfigLog2FetchWidthInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.fetch_width_in_bytes()))]
pub type CpuConfigFetchWidthInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.log2_fetches_per_cache_line()))]
pub type CpuConfigLog2FetchesPerCacheLine<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.fetches_per_cache_line()))]
pub type CpuConfigFetchesPerCacheLine<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.log2_cache_line_size_in_bytes.into()))]
pub type CpuConfigLog2CacheLineSizeInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.cache_line_size_in_bytes()))]
pub type CpuConfigCacheLineSizeInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.log2_l1_i_cache_line_count.into()))]
pub type CpuConfigLog2L1ICacheLineCount<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.l1_i_cache_line_count()))]
pub type CpuConfigL1ICacheLineCount<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.log2_l1_i_cache_size_in_bytes()))]
pub type CpuConfigLog2L1ICacheSizeInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.l1_i_cache_size_in_bytes()))]
pub type CpuConfigL1ICacheSizeInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.l1_i_cache_max_misses_in_flight.get()))]
pub type CpuConfigL1ICacheMaxMissesInFlight<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.rob_size.get()))]
pub type CpuConfigRobSize<C: PhantomConstGet<CpuConfig>> = DynSize;
pub trait PhantomConstCpuConfig:
PhantomConstGet<CpuConfig>
+ Into<PhantomConst<CpuConfig>>
+ From<PhantomConst<CpuConfig>>
+ Type
+ ToSimValue<Type = Self>
+ ToExpr<Type = Self>
{
}
impl PhantomConstCpuConfig for PhantomConst<CpuConfig> {}

View file

@ -1,4 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
pub mod simple_power_isa;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -27,432 +27,26 @@ pub struct PowerIsaCrBitNum {
impl MOpRegNum { impl MOpRegNum {
pub const POWER_ISA_LR_REG_NUM: u32 = 1; pub const POWER_ISA_LR_REG_NUM: u32 = 1;
#[hdl]
pub fn power_isa_lr_reg() -> Expr<Self> {
#[hdl]
Self {
value: Self::POWER_ISA_LR_REG_NUM.cast_to_static::<UInt<_>>(),
}
}
pub const POWER_ISA_CTR_REG_NUM: u32 = 2; pub const POWER_ISA_CTR_REG_NUM: u32 = 2;
#[hdl]
pub fn power_isa_ctr_reg() -> Expr<Self> {
#[hdl]
Self {
value: Self::POWER_ISA_CTR_REG_NUM.cast_to_static::<UInt<_>>(),
}
}
pub const POWER_ISA_TAR_REG_NUM: u32 = 3; pub const POWER_ISA_TAR_REG_NUM: u32 = 3;
#[hdl] /// XER bits are stored in [`PRegValue.flags`], bits that don't exist in [`PRegValue.flags`] are stored in [`PRegValue.int_fp`]
pub fn power_isa_tar_reg() -> Expr<Self> { ///
#[hdl] /// [`PRegValue.flags`]: struct@crate::register::PRegValue
Self { /// [`PRegValue.int_fp`]: struct@crate::register::PRegValue
value: Self::POWER_ISA_TAR_REG_NUM.cast_to_static::<UInt<_>>(), pub const POWER_ISA_XER_REG_NUM: u32 = 4;
}
}
/// SO, OV, and OV32 XER bits -- in [`PRegValue.flags`] pub const POWER_ISA_CR_REG_NUMS: Range<u32> = 8..16;
///
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
pub const POWER_ISA_XER_SO_OV_OV32_REG_NUM: u32 =
range_u32_nth_or_panic(&Self::FLAG_REG_NUMS, 0);
/// CA and CA32 XER bits -- in [`PRegValue.flags`]
///
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
pub const POWER_ISA_XER_CA_CA32_REG_NUM: u32 = 4;
/// only the XER bits that don't exist in [`PRegValue.flags`]
///
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
pub const POWER_ISA_XER_OTHER_REG_NUM: u32 = 5;
/// used as a temporary for things like computing the effective address before loading/storing memory
pub const POWER_ISA_TEMP_REG_NUM: u32 = 8;
#[hdl]
pub fn power_isa_temp_reg() -> Expr<Self> {
#[hdl]
Self {
value: Self::POWER_ISA_TEMP_REG_NUM.cast_to_static::<UInt<_>>(),
}
}
/// SO, OV, and OV32 XER bits -- in [`PRegValue.flags`]
///
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
#[hdl]
pub fn power_isa_xer_so_ov_ov32_reg() -> Expr<Self> {
#[hdl]
Self {
value: Self::POWER_ISA_XER_SO_OV_OV32_REG_NUM.cast_to_static::<UInt<_>>(),
}
}
/// CA and CA32 XER bits -- in [`PRegValue.flags`]
///
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
#[hdl]
pub fn power_isa_xer_ca_ca32_reg() -> Expr<Self> {
#[hdl]
Self {
value: Self::POWER_ISA_XER_CA_CA32_REG_NUM.cast_to_static::<UInt<_>>(),
}
}
/// only the XER bits that don't exist in [`PRegValue.flags`]
///
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
#[hdl]
pub fn power_isa_xer_other_reg() -> Expr<Self> {
#[hdl]
Self {
value: Self::POWER_ISA_XER_OTHER_REG_NUM.cast_to_static::<UInt<_>>(),
}
}
pub const POWER_ISA_CR_0_REG_NUM: u32 = range_u32_nth_or_panic(&Self::FLAG_REG_NUMS, 1);
pub const POWER_ISA_CR_1_THRU_7_REG_NUMS: Range<u32> = 9..16;
pub const fn power_isa_cr_reg_num(index: usize) -> u32 { pub const fn power_isa_cr_reg_num(index: usize) -> u32 {
if index == 0 { range_u32_nth_or_panic(&Self::POWER_ISA_CR_REG_NUMS, index)
Self::POWER_ISA_CR_0_REG_NUM
} else {
range_u32_nth_or_panic(&Self::POWER_ISA_CR_1_THRU_7_REG_NUMS, index - 1)
}
}
#[hdl]
pub fn power_isa_cr_reg(field_num: Expr<UInt<3>>) -> Expr<Self> {
#[hdl]
let power_isa_cr_reg: Self = wire();
#[hdl]
if field_num.cmp_eq(0u8) {
connect_any(power_isa_cr_reg.value, Self::POWER_ISA_CR_0_REG_NUM);
} else {
connect_any(
power_isa_cr_reg.value,
Self::POWER_ISA_CR_1_THRU_7_REG_NUMS.start - 1 + field_num,
);
}
power_isa_cr_reg
}
#[hdl]
pub fn power_isa_cr_reg_imm(index: usize) -> Expr<Self> {
#[hdl]
Self {
value: Self::power_isa_cr_reg_num(index).cast_to_static::<UInt<_>>(),
}
}
#[hdl]
pub fn power_isa_cr_reg_sim(field_num: &SimValue<UInt<3>>) -> SimValue<Self> {
#[hdl(sim)]
Self {
value: Self::power_isa_cr_reg_num(
field_num.cast_to_static::<UInt<8>>().as_int() as usize
)
.cast_to_static::<UInt<_>>(),
}
} }
pub const POWER_ISA_GPR_REG_NUMS: Range<u32> = 32..64; pub const POWER_ISA_GPR_REG_NUMS: Range<u32> = 32..64;
pub const fn power_isa_gpr_reg_num(index: usize) -> u32 { pub const fn power_isa_gpr_reg_num(index: usize) -> u32 {
range_u32_nth_or_panic(&Self::POWER_ISA_GPR_REG_NUMS, index) range_u32_nth_or_panic(&Self::POWER_ISA_GPR_REG_NUMS, index)
} }
#[hdl]
pub fn power_isa_gpr_reg(reg_num: Expr<UInt<5>>) -> Expr<Self> {
#[hdl]
Self {
value: (Self::POWER_ISA_GPR_REG_NUMS.start + reg_num).cast_to_static::<UInt<_>>(),
}
}
#[hdl]
pub fn power_isa_gpr_reg_imm(index: usize) -> Expr<Self> {
#[hdl]
Self {
value: Self::power_isa_gpr_reg_num(index).cast_to_static::<UInt<_>>(),
}
}
#[hdl]
pub fn power_isa_gpr_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> {
#[hdl(sim)]
Self {
value: (Self::POWER_ISA_GPR_REG_NUMS.start + reg_num).cast_to_static::<UInt<_>>(),
}
}
pub const fn power_isa_gpr_or_zero_reg_num(index: usize) -> u32 {
if index == 0 {
Self::CONST_ZERO_REG_NUM
} else {
Self::power_isa_gpr_reg_num(index)
}
}
#[hdl]
pub fn power_isa_gpr_or_zero_reg(reg_num: Expr<UInt<5>>) -> Expr<Self> {
#[hdl]
let power_isa_gpr_or_zero_reg: Self = wire();
connect(power_isa_gpr_or_zero_reg, Self::power_isa_gpr_reg(reg_num));
#[hdl]
if reg_num.cmp_eq(0u8) {
connect(power_isa_gpr_or_zero_reg, Self::const_zero());
}
power_isa_gpr_or_zero_reg
}
#[hdl]
pub fn power_isa_gpr_or_zero_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> {
#[hdl(sim)]
Self {
value: Self::power_isa_gpr_or_zero_reg_num(
reg_num.cast_to_static::<UInt<8>>().as_int() as usize,
)
.cast_to_static::<UInt<_>>(),
}
}
pub const POWER_ISA_FPR_REG_NUMS: Range<u32> = 64..96; pub const POWER_ISA_FPR_REG_NUMS: Range<u32> = 64..96;
pub const fn power_isa_fpr_reg_num(index: usize) -> u32 { pub const fn power_isa_fpr_reg_num(index: usize) -> u32 {
range_u32_nth_or_panic(&Self::POWER_ISA_FPR_REG_NUMS, index) range_u32_nth_or_panic(&Self::POWER_ISA_FPR_REG_NUMS, index)
} }
#[hdl]
pub fn power_isa_fpr_reg(reg_num: Expr<UInt<5>>) -> Expr<Self> {
#[hdl]
Self {
value: (Self::POWER_ISA_FPR_REG_NUMS.start + reg_num).cast_to_static::<UInt<_>>(),
}
}
#[hdl]
pub fn power_isa_fpr_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> {
#[hdl(sim)]
Self {
value: (Self::POWER_ISA_FPR_REG_NUMS.start + reg_num).cast_to_static::<UInt<_>>(),
}
}
}
#[hdl(cmp_eq)]
pub struct PowerIsaSpr {
pub num: UInt<10>,
}
macro_rules! make_spr_enum {
(
$(#[$enum_meta:meta])*
$enum_vis:vis enum $PowerIsaSprEnum:ident {
$($enum_body:tt)*
}
) => {
$(#[$enum_meta])*
$enum_vis enum $PowerIsaSprEnum {
$($enum_body)*
Unknown(u16),
}
make_spr_enum! {
@impl
$enum_vis enum $PowerIsaSprEnum {
$($enum_body)*
}
}
};
(
@impl
$enum_vis:vis enum $PowerIsaSprEnum:ident {
$(
$(#[$variant_meta:meta])*
$Variant:ident = $value:literal,
)+
}
) => {
impl $PowerIsaSprEnum {
pub const VARIANTS: &[Self; 1 << 10] = &{
let mut retval = [Self::Unknown(0); 1 << 10];
let mut i = 0;
while i < retval.len() {
retval[i] = Self::Unknown(i as u16);
i += 1;
}
let mut last_value = None;
#[track_caller]
const fn add_variant(
values: &mut [$PowerIsaSprEnum; 1 << 10],
last_value: &mut Option<u16>,
variant: $PowerIsaSprEnum,
value: u16,
) {
assert!(value < 1 << 10, "variant value out of range");
if let Some(last_value) = *last_value {
assert!(last_value < value, "variants must be in ascending order with no duplicates");
}
*last_value = Some(value);
values[value as usize] = variant;
}
$(add_variant(&mut retval, &mut last_value, Self::$Variant, $value);)+
retval
};
pub const fn value(self) -> u16 {
match self {
$(Self::$Variant => $value,)+
Self::Unknown(v) => v,
}
}
}
};
}
make_spr_enum! {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[repr(u16)]
pub enum PowerIsaSprEnum {
Xer = 1,
UserDscr = 3,
Lr = 8,
Ctr = 9,
UserAmr = 13,
Dscr = 17,
Dsisr = 18,
Dar = 19,
Dec = 22,
Srr0 = 26,
Srr1 = 27,
Cfar = 28,
Amr = 29,
Pidr = 48,
Iamr = 61,
ReadCtrl = 136,
WriteCtrl = 152,
Fscr = 153,
Uamor = 157,
Pspb = 159,
Dpdes = 176,
Dawr0 = 180,
Dawr1 = 181,
Rpr = 186,
Ciabr = 187,
Dawrx0 = 188,
Dawrx1 = 189,
Hfscr = 190,
Vrsave = 256,
UserSprg3 = 259,
Tb = 268,
Tbu = 269,
Sprg0 = 272,
Sprg1 = 273,
Sprg2 = 274,
Sprg3 = 275,
Tbl = 284,
WriteTbu = 285,
Tbu40 = 286,
Pvr = 287,
Hsprg0 = 304,
Hsprg1 = 305,
Hdsisr = 306,
Hdar = 307,
Spurr = 308,
Purr = 309,
Hdec = 310,
Hrmor = 313,
Hsrr0 = 314,
Hsrr1 = 315,
Lpcr = 318,
Lpidr = 319,
Hmer = 336,
Hmeer = 337,
Pcr = 338,
Heir = 339,
Amor = 349,
Tir = 446,
UserHdexcr = 455,
Ptcr = 464,
Hashkeyr = 468,
Hashpkeyr = 469,
Hdexcr = 471,
Usprg0 = 496,
Usprg1 = 497,
Urmor = 505,
Usrr0 = 506,
Usrr1 = 507,
Smfctrl = 511,
UserSier2 = 736,
UserSier3 = 737,
UserMmcr3 = 738,
Sier2 = 752,
Sier3 = 753,
Mmcr3 = 754,
UserSier = 768,
Mmcr2 = 769,
Mmcra = 770,
Pmc1 = 771,
Pmc2 = 772,
Pmc3 = 773,
Pmc4 = 774,
Pmc5 = 775,
Pmc6 = 776,
Mmcr0 = 779,
Siar = 780,
Sdar = 781,
Mmcr1 = 782,
Sier = 784,
PrivMmcr2 = 785,
PrivMmcra = 786,
PrivPmc1 = 787,
PrivPmc2 = 788,
PrivPmc3 = 789,
PrivPmc4 = 790,
PrivPmc5 = 791,
PrivPmc6 = 792,
PrivMmcr0 = 795,
PrivSiar = 796,
PrivSdar = 797,
PrivMmcr1 = 798,
Bescrs15 = 800,
Bescrsu16 = 801,
Bescrr15 = 802,
Bescrru16 = 803,
Ebbhr = 804,
Ebbrr = 805,
Bescr = 806,
Reserved808 = 808,
Reserved809 = 809,
Reserved810 = 810,
Reserved811 = 811,
UserDexcr = 812,
Tar = 815,
Asdr = 816,
Psscr = 823,
Dexcr = 828,
Ic = 848,
Vtb = 849,
HyperPsscr = 855,
Ppr = 896,
Ppr32 = 898,
Pir = 1023,
}
}
impl ValueType for PowerIsaSprEnum {
type Type = PowerIsaSpr;
type ValueCategory = fayalite::expr::value_category::ValueCategoryValue;
fn ty(&self) -> Self::Type {
PowerIsaSpr
}
}
impl ToExpr for PowerIsaSprEnum {
#[hdl]
fn to_expr(&self) -> Expr<Self::Type> {
#[hdl]
PowerIsaSpr {
num: self.value().cast_to_static::<UInt<_>>(),
}
}
}
impl ToSimValueWithType<PowerIsaSpr> for PowerIsaSprEnum {
fn to_sim_value_with_type(&self, _ty: PowerIsaSpr) -> SimValue<PowerIsaSpr> {
self.to_sim_value()
}
}
impl ToSimValue for PowerIsaSprEnum {
#[hdl]
fn to_sim_value(&self) -> SimValue<Self::Type> {
#[hdl(sim)]
PowerIsaSpr {
num: self.value().cast_to_static::<UInt<_>>(),
}
}
} }

View file

@ -1,12 +1,10 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
pub mod config; pub mod config;
pub mod decoder;
pub mod fetch;
pub mod instruction; pub mod instruction;
pub mod next_pc;
pub mod powerisa_instructions_xml;
pub mod reg_alloc; pub mod reg_alloc;
pub mod register; pub mod register;
pub mod unit; pub mod unit;
pub mod util; pub mod util;
//TODO read other modules
pub mod main_memory;

View file

@ -0,0 +1,60 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
// first copied code block -- changes needed
use crate::{
config::CpuConfig,
instruction::{
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, RenamedMOp,
UnitOutRegNum, mop_enum,
},
register::{FlagsMode, PRegValue},
unit::unit_base::UnitToRegAlloc,
};
use fayalite::{
bundle::{Bundle, BundleType},
intern::{Intern, Interned},
prelude::*,
};
//input address <32> bit ?
//output data word <8> bit for first test (read only)
#[hdl_module]
/// add a comment here
pub fn main_memory(config: &CpuConfig) {
#[hdl]
let addr: UInt<64> = m.input();
#[hdl]
let read_data: UInt<64> = m.output();
#[hdl]
let en: Bool = m.input();
//WIP: add write support
#[hdl]
let write_en: Bool = m.input();
#[hdl]
let write_data: UInt<64> = m.input();
#[hdl]
let cd: ClockDomain = m.input();
#[hdl] // FIXME: do not hardcode memory size and content --
//let mut my_memory = memory_with_init([0x12_hdl_u8, 0x34_hdl_u8, 0x56_hdl_u8, 0x78_hdl_u8]);
let mut my_memory = memory();
my_memory.depth(256); //TODO make configurable
let read_port = my_memory.new_read_port();
connect_any(read_port.addr, addr);
connect_any(read_port.en, addr.cmp_lt(256u64) & en); // and not write_en
connect(read_port.clk, cd.clk);
connect(read_data, read_port.data);
let write_port = my_memory.new_write_port();
connect_any(write_port.addr, addr);
connect_any(write_port.en, addr.cmp_lt(256u64) & en & write_en);
connect_any(write_port.data, write_data);
connect(write_port.clk, cd.clk);
connect_any(write_port.mask, true); //can only write 8 bits at a time
}

File diff suppressed because it is too large Load diff

View file

@ -1,24 +0,0 @@
%% SPDX-License-Identifier: LGPL-3.0-or-later
%% See Notices.txt for copyright information
stateDiagram-v2
direction LR
state "Next PC" as next_pc
[*] --> next_pc
state "Fetch/Decode" as fetch_decode
next_pc --> fetch_decode
state "Branch Predictor" as br_pred
next_pc --> br_pred
br_pred --> next_pc: cancel following
state "Post-decode" as post_decode
fetch_decode --> post_decode
br_pred --> post_decode
post_decode --> next_pc: cancel following
state "Execute/Retire" as execute_retire
post_decode --> execute_retire
execute_retire --> [*]
execute_retire --> next_pc: cancel following

File diff suppressed because it is too large Load diff

View file

@ -241,7 +241,7 @@ pub fn reg_alloc(config: &CpuConfig) {
// TODO: finish // TODO: finish
connect( connect(
rob.renamed_insns_in[fetch_index].data, rob.renamed_insns_in[fetch_index].data,
rob.ty().renamed_insns_in.element().data.HdlNone(), Expr::ty(rob).renamed_insns_in.element().data.HdlNone(),
); );
// TODO: finish // TODO: finish
connect( connect(
@ -263,7 +263,7 @@ pub fn reg_alloc(config: &CpuConfig) {
); );
connect( connect(
renamed_mops[fetch_index], renamed_mops[fetch_index],
renamed_mops.ty().element().HdlNone(), Expr::ty(renamed_mops).element().HdlNone(),
); );
#[hdl] #[hdl]
struct RenameTableReadPort<T> { struct RenameTableReadPort<T> {
@ -332,7 +332,7 @@ pub fn reg_alloc(config: &CpuConfig) {
let write_port = wire_with_loc( let write_port = wire_with_loc(
&format!("{table_name}_{fetch_index}_{}", reg_kind.reg_name()), &format!("{table_name}_{fetch_index}_{}", reg_kind.reg_name()),
SourceLocation::caller(), SourceLocation::caller(),
write_port_.ty(), Expr::ty(write_port_),
); );
connect(write_port_, write_port); connect(write_port_, write_port);
write_ports.push_back(write_port); write_ports.push_back(write_port);
@ -343,7 +343,7 @@ pub fn reg_alloc(config: &CpuConfig) {
addr: 0_hdl_u0, addr: 0_hdl_u0,
en: false, en: false,
clk: cd.clk, clk: cd.clk,
data: write_port.data.ty().uninit(), data: Expr::ty(write_port.data).uninit(),
mask: splat_mask(config.p_reg_num(), true.to_expr()), mask: splat_mask(config.p_reg_num(), true.to_expr()),
}, },
); );
@ -375,7 +375,7 @@ pub fn reg_alloc(config: &CpuConfig) {
config.renamed_mop_in_unit().TransformedMove, config.renamed_mop_in_unit().TransformedMove,
|renamed_mop, renamed_move_op: Expr<MoveRegMOp<_, _>>| { |renamed_mop, renamed_move_op: Expr<MoveRegMOp<_, _>>| {
// TODO: finish handling MoveRegMOp // TODO: finish handling MoveRegMOp
connect(renamed_mop, renamed_mop.ty().HdlNone()); connect(renamed_mop, Expr::ty(renamed_mop).HdlNone());
}, },
); );
connect( connect(
@ -429,7 +429,7 @@ pub fn reg_alloc(config: &CpuConfig) {
); );
connect( connect(
selected_unit_index_leaf, selected_unit_index_leaf,
selected_unit_index_leaf.ty().HdlNone(), Expr::ty(selected_unit_index_leaf).HdlNone(),
); );
let unit_index_wire = wire_with_loc( let unit_index_wire = wire_with_loc(
&format!("unit_index_{fetch_index}_{unit_index}"), &format!("unit_index_{fetch_index}_{unit_index}"),
@ -447,7 +447,7 @@ pub fn reg_alloc(config: &CpuConfig) {
let selected_unit_index_node = wire_with_loc( let selected_unit_index_node = wire_with_loc(
&format!("selected_unit_index_node_{fetch_index}_{state}"), &format!("selected_unit_index_node_{fetch_index}_{state}"),
SourceLocation::caller(), SourceLocation::caller(),
l.ty(), Expr::ty(l),
); );
*state += 1; *state += 1;
connect(selected_unit_index_node, l); connect(selected_unit_index_node, l);
@ -516,7 +516,7 @@ pub fn reg_alloc(config: &CpuConfig) {
connect(unit_free_regs_tracker.alloc_out[0].ready, false); connect(unit_free_regs_tracker.alloc_out[0].ready, false);
connect( connect(
unit_to_reg_alloc.input.data, unit_to_reg_alloc.input.data,
unit_to_reg_alloc.input.ty().data.HdlNone(), Expr::ty(unit_to_reg_alloc.input).data.HdlNone(),
); );
for fetch_index in 0..config.fetch_width.get() { for fetch_index in 0..config.fetch_width.get() {
#[hdl] #[hdl]
@ -550,7 +550,7 @@ pub fn reg_alloc(config: &CpuConfig) {
} else { } else {
connect( connect(
unit_to_reg_alloc.input.data, unit_to_reg_alloc.input.data,
HdlSome(unit_to_reg_alloc.input.ty().data.HdlSome.uninit()), HdlSome(Expr::ty(unit_to_reg_alloc.input).data.HdlSome.uninit()),
); );
// FIXME: add hdl_assert(cd.clk, false.to_expr(), ""); // FIXME: add hdl_assert(cd.clk, false.to_expr(), "");
} }
@ -578,8 +578,7 @@ pub fn reg_alloc(config: &CpuConfig) {
connect(unit_to_reg_alloc.unit_forwarding_info, unit_forwarding_info); connect(unit_to_reg_alloc.unit_forwarding_info, unit_forwarding_info);
connect( connect(
unit_forwarding_info.unit_output_writes[unit_index], unit_forwarding_info.unit_output_writes[unit_index],
unit_forwarding_info Expr::ty(unit_forwarding_info)
.ty()
.unit_output_writes .unit_output_writes
.element() .element()
.HdlNone(), .HdlNone(),

View file

@ -73,7 +73,7 @@ pub fn unit_free_regs_tracker(
let reduced_alloc_nums = wire_with_loc( let reduced_alloc_nums = wire_with_loc(
&format!("reduced_alloc_nums_{}_{}", range.start, range.end), &format!("reduced_alloc_nums_{}_{}", range.start, range.end),
SourceLocation::caller(), SourceLocation::caller(),
Array[UInt[l.alloc_nums.ty().element().width() + 1]][alloc_at_once.get()], Array[UInt[Expr::ty(l.alloc_nums).element().width() + 1]][alloc_at_once.get()],
); );
for alloc_index in 0..alloc_at_once.get() { for alloc_index in 0..alloc_at_once.get() {
#[hdl] #[hdl]
@ -120,7 +120,10 @@ pub fn unit_free_regs_tracker(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use fayalite::{firrtl::ExportOptions, module::transform::simplify_enums::SimplifyEnumsKind}; use fayalite::{
cli::FormalMode, firrtl::ExportOptions,
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
};
use std::num::NonZero; use std::num::NonZero;
fn test_unit_free_regs_tracker( fn test_unit_free_regs_tracker(
@ -195,7 +198,7 @@ mod tests {
} }
} }
#[hdl] #[hdl]
let free_before_alloc_array = wire(Array[free_reg.ty()][alloc_at_once.get() + 1]); let free_before_alloc_array = wire(Array[Expr::ty(free_reg)][alloc_at_once.get() + 1]);
connect(free_before_alloc_array[0], free_reg); connect(free_before_alloc_array[0], free_reg);
#[hdl] #[hdl]
let expected_alloc = wire(Array[HdlOption[reg_num_ty]][alloc_at_once.get()]); let expected_alloc = wire(Array[HdlOption[reg_num_ty]][alloc_at_once.get()]);

View file

@ -1,14 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use fayalite::prelude::*;
use crate::instruction::ConditionMode;
use fayalite::{
expr::CastToImpl,
int::{BoolOrIntType, UIntInRange},
prelude::*,
ty::StaticType,
};
use std::fmt;
#[hdl] #[hdl]
pub enum FlagsMode { pub enum FlagsMode {
@ -16,750 +8,131 @@ pub enum FlagsMode {
X86(PRegFlagsX86), X86(PRegFlagsX86),
} }
trait PRegFlagsViewTraitSealed { #[hdl(cmp_eq)]
type UnusedInner<T>: AsRef<[T]> pub struct PRegFlagsPowerISA {}
+ AsMut<[T]>
+ IntoIterator<
Item = T,
IntoIter: DoubleEndedIterator<Item = T>
+ ExactSizeIterator
+ std::iter::FusedIterator
+ Default,
>;
const UNUSED_INNER_LEN: usize;
fn unused_inner_map<T, R>(
v: Self::UnusedInner<T>,
f: impl FnMut(T) -> R,
) -> Self::UnusedInner<R>;
fn unused_inner_from_fn<T>(f: impl FnMut(usize) -> T) -> Self::UnusedInner<T>;
fn unused_inner_each_ref<T>(v: &Self::UnusedInner<T>) -> Self::UnusedInner<&T>;
fn unused_inner_each_mut<T>(v: &mut Self::UnusedInner<T>) -> Self::UnusedInner<&mut T>;
}
#[expect(private_bounds)]
pub trait PRegFlagsViewTrait: Type + PRegFlagsViewTraitSealed {
type View<T>;
fn view<T: Type>(flags: impl ToExpr<Type = PRegFlags<T>>) -> Self::View<Expr<T>>;
fn view_sim<T: Type>(flags: impl ToSimValue<Type = PRegFlags<T>>) -> Self::View<SimValue<T>>;
fn view_sim_ref<T: Type>(flags: &SimValue<PRegFlags<T>>) -> Self::View<&SimValue<T>>;
fn view_sim_mut<T: Type>(flags: &mut SimValue<PRegFlags<T>>) -> Self::View<&mut SimValue<T>>;
fn from_view<T: ToExpr>(view: Self::View<T>) -> Expr<PRegFlags<T::Type>>;
fn from_view_sim<T: ToSimValue>(view: Self::View<T>) -> SimValue<PRegFlags<T::Type>>;
fn view_unused_into_view<T>(unused: ViewUnused<T, PRegFlagsAllUnused>) -> Self::View<T>;
fn view_into_view_unused<T>(view: Self::View<T>) -> ViewUnused<T, PRegFlagsAllUnused>;
}
pub struct ViewUnused<T, V: PRegFlagsViewTrait>(V::UnusedInner<T>);
pub struct ViewUnusedIntoIter<T, V: PRegFlagsViewTrait>(
<V::UnusedInner<T> as IntoIterator>::IntoIter,
);
impl<T, V: PRegFlagsViewTrait> Iterator for ViewUnusedIntoIter<T, V> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.0.fold(init, f)
}
fn count(self) -> usize {
self.0.count()
}
fn last(self) -> Option<Self::Item> {
self.0.last()
}
}
impl<T, V: PRegFlagsViewTrait> DoubleEndedIterator for ViewUnusedIntoIter<T, V> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
fn rfold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.0.rfold(init, f)
}
}
impl<T, V: PRegFlagsViewTrait> ExactSizeIterator for ViewUnusedIntoIter<T, V> {
fn len(&self) -> usize {
self.0.len()
}
}
impl<T, V: PRegFlagsViewTrait> std::iter::FusedIterator for ViewUnusedIntoIter<T, V> {}
impl<T, V: PRegFlagsViewTrait> Default for ViewUnusedIntoIter<T, V> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T: Clone, V: PRegFlagsViewTrait> Clone for ViewUnusedIntoIter<T, V>
where
<V::UnusedInner<T> as IntoIterator>::IntoIter: Clone,
{
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T, V: PRegFlagsViewTrait> IntoIterator for ViewUnused<T, V> {
type Item = T;
type IntoIter = ViewUnusedIntoIter<T, V>;
fn into_iter(self) -> Self::IntoIter {
ViewUnusedIntoIter(self.0.into_iter())
}
}
impl<T: Default, V: PRegFlagsViewTrait> Default for ViewUnused<T, V> {
fn default() -> Self {
Self::from_fn(|_| T::default())
}
}
impl<T, V: PRegFlagsViewTrait> ViewUnused<T, V> {
pub fn iter(&self) -> std::slice::Iter<'_, T> {
self.into_iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
self.into_iter()
}
pub fn from_fn(f: impl FnMut(usize) -> T) -> Self {
ViewUnused(V::unused_inner_from_fn(f))
}
pub fn each_ref(&self) -> ViewUnused<&T, V> {
ViewUnused(V::unused_inner_each_ref(&self.0))
}
pub fn each_mut(&mut self) -> ViewUnused<&mut T, V> {
ViewUnused(V::unused_inner_each_mut(&mut self.0))
}
pub fn map<R>(self, f: impl FnMut(T) -> R) -> ViewUnused<R, V> {
ViewUnused(V::unused_inner_map(self.0, f))
}
pub fn zip<U>(self, other: ViewUnused<U, V>) -> ViewUnused<(T, U), V> {
let mut iter = self.into_iter().zip(other);
ViewUnused::from_fn(|_| iter.next().expect("known to be Some"))
}
pub fn splat(v: T) -> Self
where
T: Clone,
{
let mut v = Some(v);
Self::from_fn(|i| {
let v = if i == V::UNUSED_INNER_LEN - 1 {
v.take()
} else {
v.clone()
};
let Some(v) = v else {
unreachable!();
};
v
})
}
pub fn splat_copied(v: T) -> Self
where
T: Copy,
{
Self::from_fn(|_| v)
}
}
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<SimValue<T>, V> {
pub fn clear(&mut self) {
for i in self.iter_mut() {
SimValue::bits_mut(i).bits_mut().fill(false);
}
}
}
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<&'_ mut SimValue<T>, V> {
pub fn clear(&mut self) {
for i in self.iter_mut() {
SimValue::bits_mut(i).bits_mut().fill(false);
}
}
}
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<Expr<T>, V>
where
UInt: CastToImpl<T, ValueOutput: ToExpr>,
{
pub fn clear(self) {
for i in self {
connect(i, UInt::new_dyn(i.ty().width()).zero().cast_to(i.ty()));
}
}
}
impl<'a, T, V: PRegFlagsViewTrait> IntoIterator for &'a ViewUnused<T, V> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.as_ref().iter()
}
}
impl<'a, T, V: PRegFlagsViewTrait> IntoIterator for &'a mut ViewUnused<T, V> {
type Item = &'a mut T;
type IntoIter = std::slice::IterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.as_mut().iter_mut()
}
}
impl<T: Clone, V: PRegFlagsViewTrait> Clone for ViewUnused<T, V>
where
V::UnusedInner<T>: Clone,
{
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: Copy, V: PRegFlagsViewTrait> Copy for ViewUnused<T, V> where V::UnusedInner<T>: Copy {}
impl<T: fmt::Debug, V: PRegFlagsViewTrait> fmt::Debug for ViewUnused<T, V>
where
V::UnusedInner<T>: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("ViewUnused").field(&self.0).finish()
}
}
macro_rules! impl_view_trait {
(
$(#[$flags_mode_meta:meta])*
$flags_mode_vis:vis struct $FlagsMode:ident {}
$(#[$view_meta:meta])*
$view_vis:vis struct $View:ident {
$(#[$unused_field_meta:meta])*
$unused_vis:vis $unused:ident: ViewUnused([$($unused_field:ident),* $(,)?]),
$($(#[$view_field_meta:meta])*
$view_field_vis:vis $view_field:ident: $flags_field:ident,)*
}
) => {
$(#[$flags_mode_meta])*
$flags_mode_vis struct $FlagsMode {}
$(#[$view_meta])*
$view_vis struct $View<T> {
$(#[$unused_field_meta])*
$unused_vis $unused: ViewUnused<T, $FlagsMode>,
$($(#[$view_field_meta])*
$view_field_vis $view_field: T,)*
}
impl<T> $View<&'_ mut T> {
$view_vis const fn reborrow<'a>(&'a mut self) -> $View<&'a mut T> {
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = self;
$View {
$unused: ViewUnused([$(&mut **$unused_field,)*]),
$($view_field: &mut **$flags_field,)*
}
}
}
impl<T> $View<T> {
$view_vis fn splat(v: T) -> Self
where
T: Clone,
{
$View {
$($view_field: v.clone(),)*
$unused: ViewUnused::splat(v),
}
}
$view_vis const fn splat_copied(v: T) -> Self
where
T: Copy,
{
$View {
$($view_field: v,)*
$unused: ViewUnused([v; _]),
}
}
$view_vis fn map<R>(self, mut f: impl FnMut(T) -> R) -> $View<R> {
#![allow(unused_mut)]
let $View {
$unused,
$($view_field,)*
} = self;
$View {
$($view_field: f($view_field),)*
$unused: $unused.map(f),
}
}
$view_vis fn zip<U>(self, other: $View<U>) -> $View<(T, U)> {
struct Fields<T> {
$($unused_field: T,)*
$($flags_field: T,)*
}
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = self;
let this = Fields {
$($unused_field,)*
$($flags_field,)*
};
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = other;
let other = Fields {
$($unused_field,)*
$($flags_field,)*
};
$View {
$unused: ViewUnused([$((this.$unused_field, other.$unused_field),)*]),
$($view_field: (this.$flags_field, other.$flags_field),)*
}
}
}
impl PRegFlagsViewTraitSealed for $FlagsMode {
type UnusedInner<T> = [T; Self::UNUSED_INNER_LEN];
const UNUSED_INNER_LEN: usize = {
let v: &[&str] = &[$(stringify!($unused_field),)*];
v.len()
};
fn unused_inner_map<T, R>(
v: Self::UnusedInner<T>,
f: impl FnMut(T) -> R,
) -> Self::UnusedInner<R> {
v.map(f)
}
fn unused_inner_from_fn<T>(f: impl FnMut(usize) -> T) -> Self::UnusedInner<T> {
std::array::from_fn(f)
}
fn unused_inner_each_ref<T>(
v: &Self::UnusedInner<T>,
) -> Self::UnusedInner<&T> {
v.each_ref()
}
fn unused_inner_each_mut<T>(
v: &mut Self::UnusedInner<T>,
) -> Self::UnusedInner<&mut T> {
v.each_mut()
}
}
impl PRegFlagsViewTrait for $FlagsMode {
type View<T> = $View<T>;
#[hdl]
fn view<T: Type>(flags: impl ToExpr<Type = PRegFlags<T>>) -> Self::View<Expr<T>> {
#[hdl]
let PRegFlags::<T> {
$($unused_field,)*
$($flags_field,)*
} = flags.to_expr();
$View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
}
}
#[hdl]
fn view_sim<T: Type>(flags: impl ToSimValue<Type = PRegFlags<T>>) -> Self::View<SimValue<T>> {
#[hdl(sim)]
let PRegFlags::<T> {
$($unused_field,)*
$($flags_field,)*
} = flags.into_sim_value();
$View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
}
}
#[hdl]
fn view_sim_ref<T: Type>(flags: &SimValue<PRegFlags<T>>) -> Self::View<&SimValue<T>> {
#[hdl(sim)]
let PRegFlags::<T> {
$($unused_field,)*
$($flags_field,)*
} = flags;
$View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
}
}
#[hdl]
fn view_sim_mut<T: Type>(flags: &mut SimValue<PRegFlags<T>>) -> Self::View<&mut SimValue<T>> {
#[hdl(sim)]
let PRegFlags::<T> {
$($unused_field,)*
$($flags_field,)*
} = flags;
$View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
}
}
#[hdl]
fn from_view<T: ToExpr>(view: Self::View<T>) -> Expr<PRegFlags<T::Type>> {
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = view;
#[hdl]
PRegFlags::<_> {
$($unused_field,)*
$($flags_field,)*
}
}
#[hdl]
fn from_view_sim<T: ToSimValue>(view: Self::View<T>) -> SimValue<PRegFlags<T::Type>> {
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = view;
#[hdl(sim)]
PRegFlags::<_> {
$($unused_field,)*
$($flags_field,)*
}
}
fn view_unused_into_view<T>(unused: ViewUnused<T, PRegFlagsAllUnused>) -> Self::View<T> {
let fields = Fields::from_view_unused(unused);
$View {
$unused: ViewUnused([$(fields.$unused_field,)*]),
$($view_field: fields.$flags_field,)*
}
}
fn view_into_view_unused<T>(view: Self::View<T>) -> ViewUnused<T, PRegFlagsAllUnused> {
let $View {
$unused: ViewUnused([$($unused_field,)*]),
$($view_field: $flags_field,)*
} = view;
let fields = Fields {
$($unused_field,)*
$($flags_field,)*
};
fields.into_view_unused()
}
}
};
}
impl_view_trait! {
#[hdl(cmp_eq)]
pub struct PRegFlagsPowerISA {}
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub struct PRegFlagsPowerISAView {
pub unused: ViewUnused([]),
pub xer_ca: pwr_ca_x86_cf,
pub xer_ca32: pwr_ca32_x86_af,
pub xer_ov: pwr_ov_x86_of,
pub xer_ov32: pwr_ov32_x86_df,
pub cr_lt: pwr_cr_lt_x86_sf,
pub cr_gt: pwr_cr_gt_x86_pf,
pub cr_eq: pwr_cr_eq_x86_zf,
/// both `CR<N>.SO` and `XER.SO` since instructions that write to both always write the same value
pub so: pwr_so,
}
}
impl PRegFlagsPowerISAView<Option<usize>> {
pub const CR_BIT_LE_INDEXES: Self = {
let mut v = Self::splat_copied(None);
let bits = v.cr_bits_lsb0_mut();
let mut i = 0;
while i < bits.len() {
*bits[i] = Some(i);
i += 1;
}
v
};
pub const CR_BIT_BE_INDEXES: Self = {
let mut v = Self::splat_copied(None);
let bits = v.cr_bits_msb0_mut();
let mut i = 0;
while i < bits.len() {
*bits[i] = Some(i);
i += 1;
}
v
};
}
impl PRegFlagsPowerISAView<Option<SimValue<ConditionMode>>> {
pub fn cr_condition_modes_sim() -> Self {
PRegFlagsPowerISAView::cr_condition_modes().map(|v| v.map(ToSimValue::into_sim_value))
}
}
impl PRegFlagsPowerISAView<Option<Expr<ConditionMode>>> {
pub fn cr_condition_modes() -> Self {
Self {
unused: ViewUnused([]),
xer_ca: None,
xer_ca32: None,
xer_ov: None,
xer_ov32: None,
cr_lt: Some(ConditionMode.SLt()),
cr_gt: Some(ConditionMode.SGt()),
cr_eq: Some(ConditionMode.Eq()),
so: Some(ConditionMode.Overflow()),
}
}
}
impl<T> PRegFlagsPowerISAView<T> {
pub fn into_cr_bits_msb0(self) -> [T; 4] {
[self.cr_lt, self.cr_gt, self.cr_eq, self.so]
}
pub const fn cr_bits_msb0_ref(&self) -> [&T; 4] {
[&self.cr_lt, &self.cr_gt, &self.cr_eq, &self.so]
}
pub const fn cr_bits_msb0_mut(&mut self) -> [&mut T; 4] {
[
&mut self.cr_lt,
&mut self.cr_gt,
&mut self.cr_eq,
&mut self.so,
]
}
pub fn into_cr_bits_lsb0(self) -> [T; 4] {
let mut retval = self.into_cr_bits_msb0();
retval.reverse();
retval
}
pub const fn cr_bits_lsb0_ref(&self) -> [&T; 4] {
let [b0, b1, b2, b3] = self.cr_bits_msb0_ref();
[b3, b2, b1, b0]
}
pub const fn cr_bits_lsb0_mut(&mut self) -> [&mut T; 4] {
let [b0, b1, b2, b3] = self.cr_bits_msb0_mut();
[b3, b2, b1, b0]
}
}
impl PRegFlagsPowerISA { impl PRegFlagsPowerISA {
pub fn cr_condition_modes_msb0() -> [Expr<ConditionMode>; 4] { pub fn xer_ca(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
PRegFlagsPowerISAView::cr_condition_modes() flags.to_expr().pwr_ca_x86_cf
.into_cr_bits_msb0()
.map(|v| v.expect("known to be Some"))
} }
pub fn cr_condition_modes_lsb0() -> [Expr<ConditionMode>; 4] { pub fn xer_ca32(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
PRegFlagsPowerISAView::cr_condition_modes() flags.to_expr().pwr_ca32_x86_af
.into_cr_bits_lsb0()
.map(|v| v.expect("known to be Some"))
} }
pub fn cr_condition_modes_msb0_sim() -> [SimValue<ConditionMode>; 4] { pub fn xer_ov(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
PRegFlagsPowerISAView::cr_condition_modes_sim() flags.to_expr().pwr_ov_x86_of
.into_cr_bits_msb0()
.map(|v| v.expect("known to be Some"))
} }
pub fn cr_condition_modes_lsb0_sim() -> [SimValue<ConditionMode>; 4] { pub fn xer_ov32(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
PRegFlagsPowerISAView::cr_condition_modes_sim() flags.to_expr().pwr_ov32_x86_df
.into_cr_bits_lsb0() }
.map(|v| v.expect("known to be Some")) /// both `CR<N>.SO` and `XER.SO` since instructions that write to both always write the same value
pub fn so(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_so
}
pub fn cr_lt(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_lt_x86_sf
}
pub fn cr_gt(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_gt_x86_pf
}
pub fn cr_eq(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_eq_x86_zf
}
#[hdl]
pub fn clear_unused(flags: impl ToExpr<Type = PRegFlags>) {
// list all flags explicitly so we don't miss handling any new flags
#[hdl]
let PRegFlags {
pwr_ca_x86_cf: _,
pwr_ca32_x86_af: _,
pwr_ov_x86_of: _,
pwr_ov32_x86_df: _,
pwr_cr_lt_x86_sf: _,
pwr_cr_gt_x86_pf: _,
pwr_cr_eq_x86_zf: _,
pwr_so: _,
} = flags;
} }
} }
impl_view_trait! { #[hdl(cmp_eq)]
#[hdl(cmp_eq)] pub struct PRegFlagsX86 {}
pub struct PRegFlagsX86 {}
#[derive(Copy, Clone, Debug)] impl PRegFlagsX86 {
#[non_exhaustive] pub fn cf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
pub struct PRegFlagsX86View { flags.to_expr().pwr_ca_x86_cf
pub unused: ViewUnused([pwr_so]), }
pub cf: pwr_ca_x86_cf, pub fn zf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
pub zf: pwr_cr_eq_x86_zf, flags.to_expr().pwr_cr_eq_x86_zf
pub sf: pwr_cr_lt_x86_sf, }
pub of: pwr_ov_x86_of, pub fn sf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
pub af: pwr_ca32_x86_af, flags.to_expr().pwr_cr_lt_x86_sf
pub pf: pwr_cr_gt_x86_pf, }
pub df: pwr_ov32_x86_df, pub fn of(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ov_x86_of
}
pub fn af(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ca32_x86_af
}
pub fn pf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_gt_x86_pf
}
pub fn df(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ov32_x86_df
}
#[hdl]
pub fn clear_unused(flags: impl ToExpr<Type = PRegFlags>) {
// list all flags explicitly so we don't miss handling any new flags
#[hdl]
let PRegFlags {
pwr_ca_x86_cf: _,
pwr_ca32_x86_af: _,
pwr_ov_x86_of: _,
pwr_ov32_x86_df: _,
pwr_cr_lt_x86_sf: _,
pwr_cr_gt_x86_pf: _,
pwr_cr_eq_x86_zf: _,
pwr_so: unused1,
} = flags;
connect(unused1, false);
} }
} }
macro_rules! impl_flags { #[hdl(cmp_eq)]
( /// this is *not* the same as any particular ISA's flags register,
$(#[$struct_meta:meta])* /// on PowerISA it is a combination of some bits from XER with a single 4-bit CR field.
$struct_vis:vis struct $PRegFlags:ident<$T:ident: Type = Bool> { ///
$($field:ident: T,)* /// Accessor functions depend on the ISA:
} ///
) => { /// * PowerISA: [`struct@PRegFlagsPowerISA`]
$(#[$struct_meta])* /// * x86: [`struct@PRegFlagsX86`]
$struct_vis struct $PRegFlags<$T: Type = Bool> { pub struct PRegFlags {
$($field: $T,)* pwr_ca_x86_cf: Bool,
} pwr_ca32_x86_af: Bool,
pwr_ov_x86_of: Bool,
struct Fields<$T> { pwr_ov32_x86_df: Bool,
$($field: $T,)* pwr_cr_lt_x86_sf: Bool,
} pwr_cr_gt_x86_pf: Bool,
pwr_cr_eq_x86_zf: Bool,
impl<$T> Fields<$T> { pwr_so: Bool,
fn from_view_unused(unused: ViewUnused<$T, PRegFlagsAllUnused>) -> Self {
let ViewUnused([
$($field,)*
]) = unused;
Self {
$($field,)*
}
}
fn into_view_unused(self) -> ViewUnused<$T, PRegFlagsAllUnused> {
ViewUnused([
$(self.$field,)*
])
}
}
impl_view_trait! {
#[hdl(cmp_eq)]
pub struct PRegFlagsAllUnused {}
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub struct PRegFlagsAllUnusedView {
pub unused: ViewUnused([
$($field,)*
]),
}
}
};
}
impl_flags! {
#[hdl(cmp_eq)]
/// this is *not* the same as any particular ISA's flags register,
/// on PowerISA it is a combination of some bits from XER with a single 4-bit CR field.
///
/// Accessor functions depend on the ISA:
///
/// * PowerISA: [`struct@PRegFlagsPowerISA`]
/// * x86: [`struct@PRegFlagsX86`]
pub struct PRegFlags<T: Type = Bool> {
pwr_ca32_x86_af: T,
pwr_ca_x86_cf: T,
pwr_ov32_x86_df: T,
pwr_ov_x86_of: T,
pwr_so: T,
pwr_cr_eq_x86_zf: T,
pwr_cr_gt_x86_pf: T,
pwr_cr_lt_x86_sf: T,
}
}
impl<T: Type> PRegFlags<T> {
pub const fn field_ty(self) -> T {
self.pwr_so
}
pub fn view<V: PRegFlagsViewTrait>(flags: impl ToExpr<Type = Self>) -> V::View<Expr<T>> {
V::view(flags)
}
pub fn view_sim<V: PRegFlagsViewTrait>(
flags: impl ToSimValue<Type = Self>,
) -> V::View<SimValue<T>> {
V::view_sim(flags)
}
pub fn view_sim_ref<V: PRegFlagsViewTrait>(flags: &SimValue<Self>) -> V::View<&SimValue<T>> {
V::view_sim_ref(flags)
}
pub fn view_sim_mut<V: PRegFlagsViewTrait>(
flags: &mut SimValue<Self>,
) -> V::View<&mut SimValue<T>> {
V::view_sim_mut(flags)
}
pub fn from_view<V: PRegFlagsViewTrait>(view: V::View<impl ToExpr<Type = T>>) -> Expr<Self> {
V::from_view(view)
}
pub fn from_view_sim<V: PRegFlagsViewTrait>(
view: V::View<impl ToSimValue<Type = T>>,
) -> SimValue<Self> {
V::from_view_sim(view)
}
pub fn fields(flags: impl ToExpr<Type = Self>) -> ViewUnused<Expr<T>, PRegFlagsAllUnused> {
Self::view::<PRegFlagsAllUnused>(flags).unused
}
pub fn fields_sim(
flags: impl ToSimValue<Type = Self>,
) -> ViewUnused<SimValue<T>, PRegFlagsAllUnused> {
Self::view_sim::<PRegFlagsAllUnused>(flags).unused
}
pub fn fields_sim_ref(flags: &SimValue<Self>) -> ViewUnused<&SimValue<T>, PRegFlagsAllUnused> {
Self::view_sim_ref::<PRegFlagsAllUnused>(flags).unused
}
pub fn fields_sim_mut(
flags: &mut SimValue<Self>,
) -> ViewUnused<&mut SimValue<T>, PRegFlagsAllUnused> {
Self::view_sim_mut::<PRegFlagsAllUnused>(flags).unused
}
pub fn from_fields(
fields: ViewUnused<impl ToExpr<Type = T>, PRegFlagsAllUnused>,
) -> Expr<Self> {
Self::from_view::<PRegFlagsAllUnused>(PRegFlagsAllUnusedView { unused: fields })
}
pub fn from_fields_sim(
fields: ViewUnused<impl ToSimValue<Type = T>, PRegFlagsAllUnused>,
) -> SimValue<Self> {
Self::from_view_sim::<PRegFlagsAllUnused>(PRegFlagsAllUnusedView { unused: fields })
}
/// if trying to set all fields individually, prefer using the individual accessor
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
pub fn splat(v: impl ToExpr<Type = T>) -> Expr<Self> {
Self::from_fields(ViewUnused::splat(v.to_expr()))
}
/// if trying to set all fields individually, prefer using the individual accessor
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
pub fn splat_sim(v: impl ToSimValue<Type = T>) -> SimValue<Self> {
Self::from_fields_sim(ViewUnused::splat(v.into_sim_value()))
}
}
impl PRegFlags<UIntInRange<0, { PRegFlags::FLAG_COUNT }>> {
pub fn flag_indexes() -> SimValue<Self> {
let ty = <Self as StaticType>::TYPE.field_ty();
Self::from_fields_sim(ViewUnused::from_fn(|i| i.to_sim_value_with_type(ty)))
}
} }
impl PRegFlags { impl PRegFlags {
/// if trying to set all fields individually, prefer using the individual accessor /// if trying to set all fields individually, prefer using the individual accessor
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc. /// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
#[hdl]
pub fn zeroed() -> Expr<PRegFlags> { pub fn zeroed() -> Expr<PRegFlags> {
Self::splat(false) #[hdl]
PRegFlags {
pwr_ca_x86_cf: false,
pwr_ca32_x86_af: false,
pwr_ov_x86_of: false,
pwr_ov32_x86_df: false,
pwr_cr_lt_x86_sf: false,
pwr_cr_gt_x86_pf: false,
pwr_cr_eq_x86_zf: false,
pwr_so: false,
}
} }
/// if trying to set all fields individually, prefer using the individual accessor
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
pub fn zeroed_sim() -> SimValue<PRegFlags> {
Self::splat_sim(false)
}
pub const FLAG_COUNT: usize = PRegFlagsAllUnused::UNUSED_INNER_LEN;
} }
#[hdl(cmp_eq)] #[hdl(cmp_eq)]

View file

@ -4,9 +4,8 @@
use crate::{ use crate::{
config::CpuConfig, config::CpuConfig,
instruction::{ instruction::{
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, RenamedMOp,
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, UnitOutRegNum, UnitOutRegNum, mop_enum,
mop_enum,
}, },
register::{FlagsMode, PRegValue}, register::{FlagsMode, PRegValue},
unit::unit_base::UnitToRegAlloc, unit::unit_base::UnitToRegAlloc,
@ -16,8 +15,6 @@ use fayalite::{
intern::{Intern, Interned}, intern::{Intern, Interned},
prelude::*, prelude::*,
}; };
use serde::{Deserialize, Serialize};
use std::ops::ControlFlow;
pub mod alu_branch; pub mod alu_branch;
pub mod unit_base; pub mod unit_base;
@ -39,7 +36,7 @@ macro_rules! all_units {
} }
) => { ) => {
$(#[$enum_meta])* $(#[$enum_meta])*
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
$vis enum $UnitKind { $vis enum $UnitKind {
$( $(
$(#[$variant_meta])* $(#[$variant_meta])*
@ -55,16 +52,9 @@ macro_rules! all_units {
} }
} }
impl ValueType for $UnitKind {
type Type = $HdlUnitKind;
type ValueCategory = fayalite::expr::value_category::ValueCategoryExpr;
fn ty(&self) -> Self::Type {
$HdlUnitKind
}
}
impl ToExpr for $UnitKind { impl ToExpr for $UnitKind {
type Type = $HdlUnitKind;
fn to_expr(&self) -> Expr<Self::Type> { fn to_expr(&self) -> Expr<Self::Type> {
match self { match self {
$($UnitKind::$Unit => $HdlUnitKind.$Unit(),)* $($UnitKind::$Unit => $HdlUnitKind.$Unit(),)*
@ -85,15 +75,7 @@ macro_rules! all_units {
#[impl_mop_into = false] #[impl_mop_into = false]
#[hdl] #[hdl]
$(#[$enum_meta])* $(#[$enum_meta])*
$vis enum $UnitMOpEnum< $vis enum $UnitMOpEnum<$DestReg: Type, $SrcRegWidth: Size, #[MOp(get_ty = $transformed_move_op_get_ty)] $TransformedMoveOp: Type> {
$DestReg: Type,
$SrcRegWidth: Size,
#[MOp(get_ty = $transformed_move_op_get_ty)] $TransformedMoveOp: Type,
#[MOpVisitVariants] [
$TransformedMoveOp: MOpVisitVariants<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
$($Op: MOpVisitVariants<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,)*
]
> {
$( $(
$(#[$variant_meta])* $(#[$variant_meta])*
$Unit($Op), $Unit($Op),
@ -116,7 +98,7 @@ macro_rules! all_units {
#[hdl] #[hdl]
$vis fn $extract(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<$Op>> { $vis fn $extract(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<$Op>> {
let expr = expr.to_expr(); let expr = expr.to_expr();
let ty = expr.ty(); let ty = Expr::ty(expr);
#[hdl] #[hdl]
let $extract = wire(HdlOption[ty.$Unit]); let $extract = wire(HdlOption[ty.$Unit]);
connect($extract, HdlOption[ty.$Unit].HdlNone()); connect($extract, HdlOption[ty.$Unit].HdlNone());
@ -182,10 +164,10 @@ macro_rules! all_units {
$TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>, $TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
{ {
let this = this.to_expr(); let this = this.to_expr();
let new_ty = this.ty().with_transformed_move_op_ty(new_transformed_move_op_ty); let new_ty = Expr::ty(this).with_transformed_move_op_ty(new_transformed_move_op_ty);
#[hdl] #[hdl]
let with_transformed_move_op = wire(HdlOption[new_ty]); let with_transformed_move_op = wire(HdlOption[new_ty]);
connect(with_transformed_move_op, with_transformed_move_op.ty().HdlNone()); connect(with_transformed_move_op, Expr::ty(with_transformed_move_op).HdlNone());
// workaround #[hdl] match expanding to a loop, so you can't move variables in it // workaround #[hdl] match expanding to a loop, so you can't move variables in it
let mut connect_transformed_move_op = Some(connect_transformed_move_op); let mut connect_transformed_move_op = Some(connect_transformed_move_op);
#[hdl] #[hdl]
@ -227,7 +209,7 @@ macro_rules! all_units {
RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)] RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)]
} }
fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> { fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> {
MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(this.ty()).$BeforeUnit(this) MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$BeforeUnit(this)
} }
})* })*
@ -236,7 +218,7 @@ macro_rules! all_units {
RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)] RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)]
} }
fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> { fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> {
MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(this.ty()).$AfterUnit(this) MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$AfterUnit(this)
} }
})* })*
}; };

View file

@ -4,14 +4,10 @@
use crate::{ use crate::{
config::CpuConfig, config::CpuConfig,
instruction::{ instruction::{
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOpDefaultImm, AddSubMOp, AluBranchMOp, AluCommonMOp, COMMON_MOP_SRC_LEN, CommonMOp, LogicalMOp, MOpTrait,
CompareMOp, LogicalFlagsMOp, LogicalMOp, MOpTrait, OutputIntegerMode, ReadSpecialMOp, OutputIntegerMode, RenamedMOp, UnitOutRegNum,
RenamedMOp, ShiftRotateMOp, UnitOutRegNum,
},
register::{
FlagsMode, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, PRegFlagsX86,
PRegFlagsX86View, PRegValue, ViewUnused,
}, },
register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue},
unit::{ unit::{
DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult, DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult,
UnitResultCompleted, UnitTrait, UnitResultCompleted, UnitTrait,
@ -42,11 +38,11 @@ fn add_sub<SrcCount: KnownSize>(
add_pc, add_pc,
} = mop; } = mop;
#[hdl] #[hdl]
let AluCommonMOp::<_, _, _, _> { let AluCommonMOp::<_, _, _> {
common, common,
output_integer_mode, output_integer_mode,
} = alu_common; } = alu_common;
let imm = CommonMOpDefaultImm::as_sint_dyn(common.imm).cast_to_static::<UInt<64>>(); let imm: Expr<UInt<64>> = CommonMOp::imm(common).cast_to_static();
#[hdl] #[hdl]
let carry_in_before_inversion = wire(); let carry_in_before_inversion = wire();
connect(carry_in_before_inversion, false); connect(carry_in_before_inversion, false);
@ -60,13 +56,13 @@ fn add_sub<SrcCount: KnownSize>(
FlagsMode::PowerISA(_) => { FlagsMode::PowerISA(_) => {
connect( connect(
carry_in_before_inversion, carry_in_before_inversion,
PRegFlagsPowerISA::view(src_values[1].flags).xer_ca, PRegFlagsPowerISA::xer_ca(src_values[1].flags),
); );
} }
FlagsMode::X86(_) => { FlagsMode::X86(_) => {
connect( connect(
carry_in_before_inversion, carry_in_before_inversion,
PRegFlagsX86::view(src_values[1].flags).cf, PRegFlagsX86::cf(src_values[1].flags),
); );
} }
} }
@ -203,36 +199,27 @@ fn add_sub<SrcCount: KnownSize>(
#[hdl] #[hdl]
match flags_mode { match flags_mode {
FlagsMode::PowerISA(_) => { FlagsMode::PowerISA(_) => {
connect( PRegFlagsPowerISA::clear_unused(flags);
flags, connect(PRegFlagsPowerISA::xer_ca(flags), pwr_ca);
PRegFlagsPowerISA::from_view(PRegFlagsPowerISAView { connect(PRegFlagsPowerISA::xer_ca32(flags), pwr_ca32);
unused: ViewUnused::splat(false.to_expr()), connect(PRegFlagsPowerISA::xer_ov(flags), pwr_ov);
xer_ca: pwr_ca, connect(PRegFlagsPowerISA::xer_ov32(flags), pwr_ov32);
xer_ca32: pwr_ca32, connect(PRegFlagsPowerISA::cr_lt(flags), pwr_cr_lt);
xer_ov: pwr_ov, connect(PRegFlagsPowerISA::cr_gt(flags), pwr_cr_gt);
xer_ov32: pwr_ov32, connect(PRegFlagsPowerISA::cr_eq(flags), pwr_cr_eq);
so: pwr_so, connect(PRegFlagsPowerISA::so(flags), pwr_so);
cr_lt: pwr_cr_lt,
cr_gt: pwr_cr_gt,
cr_eq: pwr_cr_eq,
}),
);
} }
FlagsMode::X86(_) => { FlagsMode::X86(_) => {
connect( PRegFlagsX86::clear_unused(flags);
flags, connect(PRegFlagsX86::cf(flags), x86_cf);
PRegFlagsX86::from_view(PRegFlagsX86View { connect(PRegFlagsX86::af(flags), x86_af);
unused: ViewUnused::splat(false.to_expr()), connect(PRegFlagsX86::of(flags), x86_of);
cf: x86_cf, connect(PRegFlagsX86::sf(flags), x86_sf);
zf: x86_zf, connect(PRegFlagsX86::pf(flags), x86_pf);
sf: x86_sf, connect(PRegFlagsX86::zf(flags), x86_zf);
of: x86_of,
af: x86_af, // this insn doesn't write DF, so it's output isn't used for reading DF
pf: x86_pf, connect(PRegFlagsX86::df(flags), false);
// this insn doesn't write DF, so it's output isn't used for reading DF
df: false.to_expr(),
}),
);
} }
} }
#[hdl] #[hdl]
@ -243,95 +230,9 @@ fn add_sub<SrcCount: KnownSize>(
} }
} }
#[hdl]
fn logical_flags(
mop: Expr<LogicalFlagsMOp<UnitOutRegNum<DynSize>, DynSize>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl] #[hdl]
fn logical( fn logical(
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<2>>>, mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn logical_i(
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<1>>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn shift_rotate(
mop: Expr<ShiftRotateMOp<UnitOutRegNum<DynSize>, DynSize>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn compare<SrcCount: KnownSize>(
mop: Expr<CompareMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn branch<SrcCount: KnownSize>(
mop: Expr<BranchMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn read_special(
mop: Expr<ReadSpecialMOp<UnitOutRegNum<DynSize>, DynSize>>,
pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> { ) -> Expr<UnitResultCompleted<()>> {
@ -365,13 +266,16 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
let unit_base = instance(unit_base( let unit_base = instance(unit_base(
config, config,
unit_index, unit_index,
unit_to_reg_alloc.ty().input.data.HdlSome.mop, Expr::ty(unit_to_reg_alloc).input.data.HdlSome.mop,
(), (),
)); ));
connect(unit_to_reg_alloc, unit_base.unit_to_reg_alloc); connect(unit_to_reg_alloc, unit_base.unit_to_reg_alloc);
connect(unit_base.cd, cd); connect(unit_base.cd, cd);
connect(unit_base.execute_start.ready, true); connect(unit_base.execute_start.ready, true);
connect(unit_base.execute_end, unit_base.execute_end.ty().HdlNone()); connect(
unit_base.execute_end,
Expr::ty(unit_base.execute_end).HdlNone(),
);
#[hdl] #[hdl]
if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) { if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) {
#[hdl] #[hdl]
@ -418,23 +322,6 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
}, },
), ),
), ),
AluBranchMOp::<_, _>::LogicalFlags(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(logical_flags(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::Logical(mop) => connect( AluBranchMOp::<_, _>::Logical(mop) => connect(
unit_base.execute_end, unit_base.execute_end,
HdlSome( HdlSome(
@ -452,128 +339,6 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
}, },
), ),
), ),
AluBranchMOp::<_, _>::LogicalI(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(logical_i(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::ShiftRotate(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(shift_rotate(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::Compare(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(compare(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::CompareI(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(compare(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::Branch(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(branch(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::BranchI(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(branch(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::ReadSpecial(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(read_special(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
},
),
),
} }
} }
} }

View file

@ -227,7 +227,7 @@ impl InFlightOpsSummary<DynSize> {
in_flight_ops: impl ToExpr<Type = ArrayType<HdlOption<InFlightOp<MOp>>, MaxInFlight>>, in_flight_ops: impl ToExpr<Type = ArrayType<HdlOption<InFlightOp<MOp>>, MaxInFlight>>,
) -> Expr<Self> { ) -> Expr<Self> {
let in_flight_ops = in_flight_ops.to_expr(); let in_flight_ops = in_flight_ops.to_expr();
let max_in_flight = in_flight_ops.ty().len(); let max_in_flight = Expr::ty(in_flight_ops).len();
let index_range = 0..max_in_flight; let index_range = 0..max_in_flight;
let index_ty = UInt::range(index_range.clone()); let index_ty = UInt::range(index_range.clone());
tree_reduce( tree_reduce(
@ -259,7 +259,7 @@ pub fn unit_base<
let execute_end: HdlOption<ExecuteEnd<DynSize, ExtraOut>> = let execute_end: HdlOption<ExecuteEnd<DynSize, ExtraOut>> =
m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][extra_out_ty]]); m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][extra_out_ty]]);
connect(execute_start.data, execute_start.ty().data.HdlNone()); connect(execute_start.data, Expr::ty(execute_start).data.HdlNone());
let max_in_flight = config.unit_max_in_flight(unit_index).get(); let max_in_flight = config.unit_max_in_flight(unit_index).get();
let in_flight_op_ty = InFlightOp[mop_ty]; let in_flight_op_ty = InFlightOp[mop_ty];
@ -270,7 +270,7 @@ pub fn unit_base<
let in_flight_ops_summary_value = InFlightOpsSummary::summarize(in_flight_ops); let in_flight_ops_summary_value = InFlightOpsSummary::summarize(in_flight_ops);
#[hdl] #[hdl]
let in_flight_ops_summary = wire(in_flight_ops_summary_value.ty()); let in_flight_ops_summary = wire(Expr::ty(in_flight_ops_summary_value));
connect(in_flight_ops_summary, in_flight_ops_summary_value); connect(in_flight_ops_summary, in_flight_ops_summary_value);
connect( connect(
@ -302,7 +302,7 @@ pub fn unit_base<
#[hdl] #[hdl]
let input_src_regs_valid = wire(); let input_src_regs_valid = wire();
connect(input_src_regs_valid, [true; COMMON_MOP_SRC_LEN]); connect(input_src_regs_valid, [true; COMMON_MOP_SRC_LEN]);
let mut unit_output_regs_valid: Vec<MemBuilder<Bool>> = (0..unit_output_writes.ty().len()) let mut unit_output_regs_valid: Vec<MemBuilder<Bool>> = (0..Expr::ty(unit_output_writes).len())
.map(|unit_index| { .map(|unit_index| {
let mut mem = memory_with_loc( let mut mem = memory_with_loc(
&format!("unit_{unit_index}_output_regs_valid"), &format!("unit_{unit_index}_output_regs_valid"),
@ -313,7 +313,7 @@ pub fn unit_base<
mem mem
}) })
.collect(); .collect();
for unit_index in 0..unit_output_writes.ty().len() { for unit_index in 0..Expr::ty(unit_output_writes).len() {
let mut unit_output_regs = memory_with_loc( let mut unit_output_regs = memory_with_loc(
&format!("unit_{unit_index}_output_regs"), &format!("unit_{unit_index}_output_regs"),
PRegValue, PRegValue,
@ -411,7 +411,7 @@ pub fn unit_base<
connect( connect(
unit_to_reg_alloc.output, unit_to_reg_alloc.output,
unit_to_reg_alloc.output.ty().HdlNone(), Expr::ty(unit_to_reg_alloc.output).HdlNone(),
); );
#[hdl] #[hdl]
@ -503,7 +503,7 @@ pub fn unit_base<
#[hdl] #[hdl]
if in_flight_ops_summary.ready_op_index.cmp_eq(HdlSome( if in_flight_ops_summary.ready_op_index.cmp_eq(HdlSome(
in_flight_op_index.cast_to(in_flight_ops_summary.ty().ready_op_index.HdlSome), in_flight_op_index.cast_to(Expr::ty(in_flight_ops_summary).ready_op_index.HdlSome),
)) { )) {
connect(read_src_regs, src_regs); connect(read_src_regs, src_regs);
} }
@ -512,7 +512,7 @@ pub fn unit_base<
in_flight_op_next_src_ready_flags[in_flight_op_index], in_flight_op_next_src_ready_flags[in_flight_op_index],
src_ready_flags, src_ready_flags,
); );
for unit_index in 0..unit_output_writes.ty().len() { for unit_index in 0..Expr::ty(unit_output_writes).len() {
#[hdl] #[hdl]
if let HdlSome(unit_output_write) = unit_output_writes[unit_index] { if let HdlSome(unit_output_write) = unit_output_writes[unit_index] {
#[hdl] #[hdl]

View file

@ -1,9 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use fayalite::{expr::ops::ArrayLiteral, module::wire_with_loc, prelude::*};
use std::num::NonZero;
pub mod array_vec; pub mod array_vec;
pub mod tree_reduce; pub mod tree_reduce;
@ -28,255 +25,3 @@ pub(crate) const fn range_u32_nth_or_panic(range: &std::ops::Range<u32>, index:
panic!("index out of range") panic!("index out of range")
} }
} }
// TODO: move to fayalite
pub trait Rotate<Amount> {
type Output;
/// like [`usize::rotate_left`] or [`<[T]>::rotate_left`](slice::rotate_left) depending on `Self` -- note that in lsb0 those rotate in opposite directions
fn rotate_left(&self, amount: Amount) -> Self::Output;
/// like [`usize::rotate_right`] or [`<[T]>::rotate_right`](slice::rotate_right) depending on `Self` -- note that in lsb0 those rotate in opposite directions
fn rotate_right(&self, amount: Amount) -> Self::Output;
}
impl<VSz: Size> Rotate<usize> for Expr<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
fn rotate_left(&self, amount: usize) -> Self::Output {
if self.ty().width() == 0 {
return *self;
}
let amount = amount % self.ty().width();
let l = *self << amount;
let r = *self >> (self.ty().width() - amount);
(l | r).cast_to(self.ty())
}
/// like [`usize::rotate_right`]
fn rotate_right(&self, amount: usize) -> Self::Output {
if self.ty().width() == 0 {
return *self;
}
let amount = amount % self.ty().width();
let l = *self << (self.ty().width() - amount);
let r = *self >> amount;
(l | r).cast_to(self.ty())
}
}
impl<VSz: Size> Rotate<usize> for SimValue<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
fn rotate_left(&self, amount: usize) -> Self::Output {
if self.ty().width() == 0 {
return self.clone();
}
let amount = amount % self.ty().width();
let l = self << amount;
let r = self >> (self.ty().width() - amount);
(l | r).cast_to(self.ty())
}
/// like [`usize::rotate_right`]
fn rotate_right(&self, amount: usize) -> Self::Output {
if self.ty().width() == 0 {
return self.clone();
}
let amount = amount % self.ty().width();
let l = self << (self.ty().width() - amount);
let r = self >> amount;
(l | r).cast_to(self.ty())
}
}
impl<VSz: Size, ASz: Size> Rotate<Expr<UIntType<ASz>>> for Expr<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
fn rotate_left(&self, amount: Expr<UIntType<ASz>>) -> Self::Output {
if self.ty().width() == 0 {
return *self;
}
let amount = amount % self.ty().width();
let l = *self << amount;
let r = *self >> (self.ty().width() - amount);
(l | r).cast_to(self.ty())
}
/// like [`usize::rotate_right`]
fn rotate_right(&self, amount: Expr<UIntType<ASz>>) -> Self::Output {
if self.ty().width() == 0 {
return *self;
}
let amount = amount % self.ty().width();
let l = *self << (self.ty().width() - amount).cast_to(amount.ty());
let r = *self >> amount;
(l | r).cast_to(self.ty())
}
}
impl<VSz: Size, ASz: Size> Rotate<SimValue<UIntType<ASz>>> for SimValue<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
fn rotate_left(&self, amount: SimValue<UIntType<ASz>>) -> Self::Output {
if self.ty().width() == 0 {
return self.clone();
}
let amount = amount % self.ty().width();
let l = self << &amount;
let r = self >> (self.ty().width() - amount);
(l | r).cast_to(self.ty())
}
/// like [`usize::rotate_right`]
fn rotate_right(&self, amount: SimValue<UIntType<ASz>>) -> Self::Output {
if self.ty().width() == 0 {
return self.clone();
}
let amount = amount % self.ty().width();
let l = self << (self.ty().width() - &amount).cast_to(amount.ty());
let r = self >> amount;
(l | r).cast_to(self.ty())
}
}
fn array_rotate_helper<T: Type, N: Size, AmountSize: Size>(
mut array: Expr<ArrayType<T, N>>,
amount: Expr<UIntType<AmountSize>>,
rotate_fn: impl Fn(&mut [Expr<T>], usize),
rotate_fn_name: &str,
) -> Expr<ArrayType<T, N>> {
let Some(mut prev_step_size) = NonZero::new(array.ty().len()) else {
return array;
};
fn named<T: Type>(v: Expr<T>, name: impl AsRef<str>) -> Expr<T> {
let w = wire_with_loc(name.as_ref(), SourceLocation::caller(), v.ty());
connect(w, v);
w
}
fn non_empty_array_to_expr<T: Type, Len: Size>(
v: impl AsRef<[Expr<T>]>,
) -> Expr<ArrayType<T, Len>> {
let v = v.as_ref();
ArrayLiteral::new(v[0].ty(), v.iter().map(|v| Expr::canonical(*v)).collect()).to_expr()
}
fn mux<T: Type>(b: Expr<Bool>, true_v: Expr<T>, false_v: Expr<T>) -> Expr<T> {
let a: Expr<Array<T, 2>> = non_empty_array_to_expr([false_v, true_v]);
a[b.cast_to_static::<UInt<1>>()]
}
let amount_ty = amount.ty();
let mut amount = (amount % prev_step_size).cast_to(amount_ty);
loop {
(prev_step_size, amount, array) =
if let Some(step_size) = NonZero::new(prev_step_size.get() / 2) {
let amount = named(amount, format!("{rotate_fn_name}_amount_{prev_step_size}"));
let do_rotate = amount.cmp_ge(step_size);
let mut rotated_array = (*array).clone();
rotate_fn(rotated_array.as_mut(), step_size.get());
let rotated_array = named(
non_empty_array_to_expr(rotated_array),
format!("{rotate_fn_name}_rotated_array_{step_size}"),
);
let array = mux(do_rotate, rotated_array, array);
let array = named(array, format!("{rotate_fn_name}_array_{step_size}"));
let amount = mux(do_rotate, (amount - step_size).cast_to(amount_ty), amount);
(step_size, amount, array)
} else {
return array;
};
}
}
impl<T: Type, N: Size, AmountSize: Size> Rotate<Expr<UIntType<AmountSize>>>
for Expr<ArrayType<T, N>>
{
type Output = Self;
/// like [`<[T]>::rotate_left`](slice::rotate_left)
fn rotate_left(&self, amount: Expr<UIntType<AmountSize>>) -> Self::Output {
array_rotate_helper(*self, amount, <[Expr<T>]>::rotate_left, "rotate_left")
}
/// like [`<[T]>::rotate_right`](slice::rotate_right)
fn rotate_right(&self, amount: Expr<UIntType<AmountSize>>) -> Self::Output {
array_rotate_helper(*self, amount, <[Expr<T>]>::rotate_right, "rotate_right")
}
}
impl<T: Type, N: Size, AmountSize: Size> Rotate<SimValue<UIntType<AmountSize>>>
for SimValue<ArrayType<T, N>>
{
type Output = Self;
/// like [`<[T]>::rotate_left`](slice::rotate_left)
fn rotate_left(&self, amount: SimValue<UIntType<AmountSize>>) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let Ok(amount) = usize::try_from(amount.to_bigint() % self.ty().len()) else {
unreachable!();
};
let mut retval = self.clone();
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_left(amount);
retval
}
/// like [`<[T]>::rotate_right`](slice::rotate_right)
fn rotate_right(&self, amount: SimValue<UIntType<AmountSize>>) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let Ok(amount) = usize::try_from(amount.to_bigint() % self.ty().len()) else {
unreachable!();
};
let mut retval = self.clone();
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_right(amount);
retval
}
}
impl<T: Type, N: Size> Rotate<usize> for Expr<ArrayType<T, N>> {
type Output = Self;
/// like [`<[T]>::rotate_left`](slice::rotate_left)
fn rotate_left(&self, amount: usize) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let amount = amount % self.ty().len();
let mut retval = Vec::from_iter(*self);
retval.rotate_left(amount);
ArrayLiteral::new(
self.ty().element(),
retval.into_iter().map(Expr::canonical).collect(),
)
.to_expr()
}
/// like [`<[T]>::rotate_right`](slice::rotate_right)
fn rotate_right(&self, amount: usize) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let amount = amount % self.ty().len();
let mut retval = Vec::from_iter(*self);
retval.rotate_right(amount);
ArrayLiteral::new(
self.ty().element(),
retval.into_iter().map(Expr::canonical).collect(),
)
.to_expr()
}
}
impl<T: Type, N: Size> Rotate<usize> for SimValue<ArrayType<T, N>> {
type Output = Self;
/// like [`<[T]>::rotate_left`](slice::rotate_left)
fn rotate_left(&self, amount: usize) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let amount = amount % self.ty().len();
let mut retval = self.clone();
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_left(amount);
retval
}
/// like [`<[T]>::rotate_right`](slice::rotate_right)
fn rotate_right(&self, amount: usize) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let amount = amount % self.ty().len();
let mut retval = self.clone();
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_right(amount);
retval
}
}

View file

@ -1,28 +1,147 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use fayalite::{expr::ops::ExprIndex, int::UIntInRangeInclusiveType, prelude::*}; use fayalite::{
use std::fmt; expr::ops::{ExprCastTo, ExprIndex, ExprPartialEq, ExprPartialOrd},
int::SizeType,
intern::{Intern, Interned},
prelude::*,
ty::{MatchVariantWithoutScope, StaticType, TypeProperties},
};
use std::{marker::PhantomData, ops::Index};
#[derive(Clone, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct ArrayVecFullError<V, I: Iterator> { pub struct Length<Max: Size> {
pub value: V, ty: UInt,
pub rest: std::iter::Chain<std::iter::Once<I::Item>, I>, _phantom: PhantomData<Max>,
} }
impl<V, I: Iterator> fmt::Display for ArrayVecFullError<V, I> { impl<Max: Size> Length<Max> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub fn new(max: Max::SizeType) -> Self {
write!(f, "ArrayVec is full") Self {
ty: UInt::range_inclusive(0..=Max::as_usize(max)),
_phantom: PhantomData,
}
}
pub fn ty(self) -> UInt {
self.ty
}
pub fn zero(self) -> Expr<Self> {
Self::from_uint_unchecked(self.ty.zero())
}
pub fn from_uint_unchecked(v: impl ToExpr<Type = UInt>) -> Expr<Self> {
Expr::from_canonical(Expr::canonical(v.to_expr()))
}
pub fn cast_from_uint_unchecked<SrcWidth: Size>(
self,
v: impl ToExpr<Type = UIntType<SrcWidth>>,
) -> Expr<Self> {
Self::from_uint_unchecked(v.to_expr().cast_to(self.ty))
}
pub fn as_uint(this: impl ToExpr<Type = Self>) -> Expr<UInt> {
let this = this.to_expr();
this.cast_to(Expr::ty(this).ty)
} }
} }
impl<V: fmt::Debug, I: Iterator<Item: fmt::Debug> + fmt::Debug> std::error::Error impl<Max: Size, DestWidth: Size> ExprCastTo<UIntType<DestWidth>> for Length<Max> {
for ArrayVecFullError<V, I> fn cast_to(src: Expr<Self>, to_type: UIntType<DestWidth>) -> Expr<UIntType<DestWidth>> {
{ Expr::<UInt>::from_canonical(Expr::canonical(src)).cast_to(to_type)
}
} }
#[hdl] #[allow(non_upper_case_globals)]
pub type Length<Max: Size> = UIntInRangeInclusiveType<ConstUsize<0>, Max>; pub const Length: __LengthWithoutGenerics = __LengthWithoutGenerics {};
#[non_exhaustive]
pub struct __LengthWithoutGenerics {}
impl<M: SizeType> Index<M> for __LengthWithoutGenerics {
type Output = Length<M::Size>;
fn index(&self, max: M) -> &Self::Output {
Interned::into_inner(Length::new(max).intern_sized())
}
}
impl<Max: Size> Type for Length<Max> {
type BaseType = UInt;
type MaskType = Bool;
type MatchVariant = Expr<Self>;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants(
this: Expr<Self>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter {
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(this))
}
fn mask_type(&self) -> Self::MaskType {
Bool
}
fn canonical(&self) -> CanonicalType {
self.ty.canonical()
}
fn from_canonical(canonical_type: CanonicalType) -> Self {
let ty = <UInt>::from_canonical(canonical_type);
if let Some(known_max) = Max::KNOWN_VALUE {
assert_eq!(ty, UInt::range_inclusive(0..=known_max));
}
Self {
ty,
_phantom: PhantomData,
}
}
fn source_location() -> SourceLocation {
SourceLocation::caller()
}
}
impl<Max: KnownSize> StaticType for Length<Max> {
const TYPE: Self = Self {
ty: UInt {
width: Max::VALUE.next_power_of_two().ilog2() as usize,
},
_phantom: PhantomData,
};
const MASK_TYPE: Self::MaskType = Bool;
const TYPE_PROPERTIES: TypeProperties = {
let mut p = <UInt<1>>::TYPE_PROPERTIES;
p.bit_width = Self::TYPE.ty.width;
p
};
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
}
impl<Max: Size> ExprPartialEq<Self> for Length<Max> {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
Self::as_uint(lhs).cmp_eq(Self::as_uint(rhs))
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
Self::as_uint(lhs).cmp_ne(Self::as_uint(rhs))
}
}
impl<Max: Size> ExprPartialOrd<Self> for Length<Max> {
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
Self::as_uint(lhs).cmp_lt(Self::as_uint(rhs))
}
fn cmp_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
Self::as_uint(lhs).cmp_le(Self::as_uint(rhs))
}
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
Self::as_uint(lhs).cmp_gt(Self::as_uint(rhs))
}
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
Self::as_uint(lhs).cmp_ge(Self::as_uint(rhs))
}
}
/// like [`std::vec::Vec`], except with a [`Expr`] for [`len()`][`Self::len()`] and a fixed capacity /// like [`std::vec::Vec`], except with a [`Expr`] for [`len()`][`Self::len()`] and a fixed capacity
#[hdl] #[hdl]
@ -37,55 +156,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
#[hdl] #[hdl]
ArrayVec { ArrayVec {
elements: self.elements.uninit(), elements: self.elements.uninit(),
len: 0u8.cast_to(self.len), len: self.len.zero(),
}
}
#[hdl]
pub fn new_sim(self, uninit_element: impl ToSimValueWithType<T>) -> SimValue<Self> {
let uninit_element = uninit_element.into_sim_value_with_type(self.element());
#[hdl(sim)]
ArrayVec::<_, _> {
elements: SimValue::from_array_elements(
self.elements,
(0..self.elements.len()).map(|_| uninit_element.clone()),
),
len: 0u8.cast_to(self.len),
}
}
#[hdl]
pub fn new_full_sim(
self,
elements: impl ToSimValueWithType<ArrayType<T, N>>,
) -> SimValue<Self> {
let elements = elements.to_sim_value_with_type(self.elements);
#[hdl(sim)]
Self {
elements,
len: self.elements.len().to_sim_value_with_type(self.len),
}
}
pub fn from_iter_sim<I: IntoIterator<Item: ToSimValueWithType<T>>>(
self,
uninit_element: impl ToSimValueWithType<T>,
iter: I,
) -> Result<SimValue<Self>, ArrayVecFullError<SimValue<Self>, I::IntoIter>> {
let mut value = Self::new_sim(self, uninit_element);
let element = self.element();
let mut iter = iter.into_iter();
for i in 0..self.capacity() {
let Some(v) = iter.next() else {
break;
};
value.elements[i] = v.into_sim_value_with_type(element);
*value.len = i + 1;
}
if let Some(extra) = iter.next() {
Err(ArrayVecFullError {
value,
rest: std::iter::once(extra).chain(iter),
})
} else {
Ok(value)
} }
} }
pub fn element(self) -> T { pub fn element(self) -> T {
@ -105,8 +176,8 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
let elements = elements.to_expr(); let elements = elements.to_expr();
let len = len.to_expr(); let len = len.to_expr();
assert_eq!( assert_eq!(
Length[N::from_usize(elements.ty().len())], Length::new(N::from_usize(Expr::ty(elements).len())),
len.ty(), Expr::ty(len),
"len type mismatch", "len type mismatch",
); );
#[hdl] #[hdl]
@ -118,12 +189,9 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
pub fn len(this: impl ToExpr<Type = Self>) -> Expr<Length<N>> { pub fn len(this: impl ToExpr<Type = Self>) -> Expr<Length<N>> {
this.to_expr().len this.to_expr().len
} }
pub fn len_sim(this: &SimValue<Self>) -> &SimValue<Length<N>> {
&this.len
}
pub fn is_empty(this: impl ToExpr<Type = Self>) -> Expr<Bool> { pub fn is_empty(this: impl ToExpr<Type = Self>) -> Expr<Bool> {
let len = Self::len(this); let len = Self::len(this);
len.cmp_eq(0u8) len.cmp_eq(Expr::ty(len).zero())
} }
pub fn capacity(self) -> usize { pub fn capacity(self) -> usize {
self.elements.len() self.elements.len()
@ -139,77 +207,11 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
let this = this.to_expr(); let this = this.to_expr();
for (index, element) in this.elements.into_iter().enumerate() { for (index, element) in this.elements.into_iter().enumerate() {
#[hdl] #[hdl]
if index.cmp_lt(this.len) { if index.cmp_lt(Length::as_uint(this.len)) {
f(index, element); f(index, element);
} }
} }
} }
pub fn elements_sim_ref(this: &SimValue<Self>) -> &[SimValue<T>] {
&this.elements[..*this.len]
}
pub fn elements_sim_mut(this: &mut SimValue<Self>) -> &mut [SimValue<T>] {
let len = *this.len;
&mut this.elements[..len]
}
#[hdl]
pub async fn async_for_each_sim(
this: impl ToSimValue<Type = Self>,
mut f: impl AsyncFnMut(usize, SimValue<T>),
) {
#[hdl(sim)]
let ArrayVec::<_, _> { elements, len } = this.into_sim_value();
for (index, element) in elements.into_iter().enumerate() {
if index.cmp_lt(*len) {
f(index, element).await;
}
}
}
#[hdl]
pub async fn async_for_each_sim_ref<'a>(
this: &'a SimValue<Self>,
mut f: impl AsyncFnMut(usize, &'a SimValue<T>),
) {
#[hdl(sim)]
let ArrayVec::<_, _> { elements, len } = this;
for (index, element) in elements.iter().enumerate() {
if index.cmp_lt(**len) {
f(index, element).await;
}
}
}
#[hdl]
pub async fn async_for_each_sim_mut<'a>(
this: &'a mut SimValue<Self>,
mut f: impl AsyncFnMut(usize, &'a mut SimValue<T>),
) {
#[hdl(sim)]
let ArrayVec::<_, _> { elements, len } = this;
for (index, element) in elements.iter_mut().enumerate() {
if index.cmp_lt(**len) {
f(index, element).await;
}
}
}
#[hdl]
pub fn try_push_sim(
this: &mut SimValue<Self>,
value: impl ToSimValueWithType<T>,
) -> Result<(), SimValue<T>> {
let value = value.into_sim_value_with_type(this.ty().element());
let capacity = this.ty().capacity();
#[hdl(sim)]
let ArrayVec::<_, _> { elements, len } = this;
if **len < capacity {
elements[**len] = value;
**len += 1;
Ok(())
} else {
Err(value)
}
}
pub fn truncate_sim(this: &mut SimValue<Self>, len: usize) {
*this.len = len.min(*this.len);
}
pub fn mapped_ty<U: Type>(self, new_element_ty: U) -> ArrayVec<U, N> { pub fn mapped_ty<U: Type>(self, new_element_ty: U) -> ArrayVec<U, N> {
ArrayVec { ArrayVec {
elements: ArrayType[new_element_ty][N::from_usize(self.elements.len())], elements: ArrayType[new_element_ty][N::from_usize(self.elements.len())],
@ -224,7 +226,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
) -> Expr<ArrayVec<U, N>> { ) -> Expr<ArrayVec<U, N>> {
let this = this.to_expr(); let this = this.to_expr();
#[hdl] #[hdl]
let mapped_array_vec = wire(this.ty().mapped_ty(new_element_ty)); let mapped_array_vec = wire(Expr::ty(this).mapped_ty(new_element_ty));
connect(mapped_array_vec.len, this.len); connect(mapped_array_vec.len, this.len);
Self::for_each(this, |index, element| { Self::for_each(this, |index, element| {
connect(mapped_array_vec[index], f(index, element)); connect(mapped_array_vec[index], f(index, element));
@ -232,42 +234,15 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
mapped_array_vec mapped_array_vec
} }
#[hdl] #[hdl]
pub fn map_sim<U: Type>(
this: impl ToSimValue<Type = Self>,
uninit_element: impl ToSimValue<Type = U>,
mut f: impl FnMut(usize, SimValue<T>) -> SimValue<U>,
) -> SimValue<ArrayVec<U, N>> {
let this = this.into_sim_value();
let uninit_element = uninit_element.into_sim_value();
let ty = this.ty().mapped_ty(uninit_element.ty());
#[hdl(sim)]
let Self { elements, len } = this;
#[hdl(sim)]
ArrayVec::<_, _> {
elements: SimValue::from_array_elements(
ty.elements,
SimValue::into_value(elements)
.into_iter()
.enumerate()
.map(|(index, element)| {
if index < *len {
f(index, element)
} else {
uninit_element.clone()
}
}),
),
len,
}
}
#[hdl]
pub fn as_array_of_options(this: impl ToExpr<Type = Self>) -> Expr<ArrayType<HdlOption<T>, N>> { pub fn as_array_of_options(this: impl ToExpr<Type = Self>) -> Expr<ArrayType<HdlOption<T>, N>> {
let this = this.to_expr(); let this = this.to_expr();
#[hdl] #[hdl]
let array_vec_as_array_of_options = let array_vec_as_array_of_options = wire(
wire(ArrayType[HdlOption[this.ty().element()]][N::from_usize(this.ty().capacity())]); ArrayType[HdlOption[Expr::ty(this).element()]]
[N::from_usize(Expr::ty(this).capacity())],
);
for element in array_vec_as_array_of_options { for element in array_vec_as_array_of_options {
connect(element, element.ty().HdlNone()); connect(element, Expr::ty(element).HdlNone());
} }
Self::for_each(this, |index, element| { Self::for_each(this, |index, element| {
connect(array_vec_as_array_of_options[index], HdlSome(element)) connect(array_vec_as_array_of_options[index], HdlSome(element))
@ -288,34 +263,3 @@ where
<ArrayType<T, N> as ExprIndex<Idx>>::expr_index(&this.elements, index) <ArrayType<T, N> as ExprIndex<Idx>>::expr_index(&this.elements, index)
} }
} }
impl<T: Type> ArrayVec<T, ConstUsize<1>> {
#[hdl]
pub fn from_opt_sim(
opt: impl ToSimValue<Type = HdlOption<T>>,
uninit_element: impl ToSimValueWithType<T>,
) -> SimValue<Self> {
let opt = opt.into_sim_value();
let ty = ArrayVec[opt.ty().HdlSome][ConstUsize];
#[hdl(sim)]
match opt {
HdlSome(v) => ty.new_full_sim([v]),
HdlNone => ty.new_sim(uninit_element),
}
}
#[hdl]
pub fn into_opt_sim(this: impl ToSimValue<Type = Self>) -> SimValue<HdlOption<T>> {
let this = this.into_sim_value();
#[hdl(sim)]
let Self { elements, len } = this;
let [element] = SimValue::into_value(elements);
let ty = HdlOption[element.ty()];
if *len == 0 {
#[hdl(sim)]
ty.HdlNone()
} else {
#[hdl(sim)]
ty.HdlSome(element)
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,697 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use cpu::{
config::{CpuConfig, UnitConfig},
fetch::{
FetchToDecodeInterface, FetchToDecodeInterfaceInner, MemoryInterface,
MemoryOperationErrorKind, MemoryOperationFinish, MemoryOperationFinishKind,
MemoryOperationKind, MemoryOperationStart, fetch,
},
next_pc::{FETCH_BLOCK_ID_WIDTH, NextPcToFetchInterface, NextPcToFetchInterfaceInner},
unit::UnitKind,
util::array_vec::ArrayVec,
};
use fayalite::{
prelude::*,
sim::vcd::VcdWriterDecls,
util::{DebugAsDisplay, RcWriter},
};
use std::{cell::RefCell, collections::VecDeque, fmt, num::NonZeroUsize};
struct Random {
index: u64,
}
impl Random {
fn next(&mut self) -> u64 {
let index = self.index;
self.index = self.index.wrapping_add(1);
// make a pseudo-random number deterministically based on index
index
.wrapping_add(1)
.wrapping_mul(0x18C49126EABE7A0D) // random prime
.rotate_left(32)
.wrapping_mul(0x92B38C197608A6B) // random prime
.rotate_right(60)
}
}
const MEMORY_QUEUE_SIZE: usize = 16;
#[hdl]
struct MemoryQueueEntry {
addr: UInt<64>,
fetch_block_id: UInt<{ FETCH_BLOCK_ID_WIDTH }>,
cycles_left: UInt<8>,
}
impl MemoryQueueEntry {
#[hdl]
fn default_sim(self) -> SimValue<Self> {
#[hdl(sim)]
Self {
addr: 0u64,
fetch_block_id: self.fetch_block_id.zero(),
cycles_left: 0u8,
}
}
fn get_next_delay(random: &mut Random) -> u8 {
if random.next() % 32 == 0 { 30 } else { 5 }
}
}
const MEMORY_DATA: &str = "Test data, testing...\nTest Test!\nSecond Cache Line\nTesting.....\n";
const MEMORY_START: u64 = 0x1000;
const MEMORY_RANGE2: std::ops::Range<u64> = 0x2000..0x3000;
const MEMORY_ERROR_RANGE: std::ops::Range<u64> = 0x10F00..0x20F00;
const MEMORY_ERROR_STEP: u64 = 0x1000;
fn read_memory(start: u64, len: usize) -> Option<&'static [u8]> {
if MEMORY_ERROR_RANGE.contains(&start) {
let start = start - MEMORY_ERROR_RANGE.start;
let fail_at = start / MEMORY_ERROR_STEP;
let offset = start % MEMORY_ERROR_STEP;
return if offset < fail_at {
[0xFFu8; MEMORY_DATA.len()].get(..len)
} else {
None
};
}
if MEMORY_RANGE2.contains(&start) {
return [0xF2u8; MEMORY_DATA.len()].get(..len);
}
MEMORY_DATA
.as_bytes()
.get(start.checked_sub(MEMORY_START)?.try_into().ok()?..)?
.get(..len)
}
#[hdl_module(extern)]
fn mock_memory(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> =
m.input(MemoryInterface[config]);
#[hdl]
let queue_debug: ArrayVec<MemoryQueueEntry, ConstUsize<{ MEMORY_QUEUE_SIZE }>> = m.output();
m.register_clock_for_past(cd.clk);
m.extern_module_simulation_fn(
(cd, memory_interface, queue_debug),
|(cd, memory_interface, queue_debug), mut sim| async move {
// intentionally have a different sequence each time we're reset
let random = RefCell::new(Random { index: 0 });
sim.resettable(
cd,
async |mut sim| {
#[hdl]
let MemoryInterface::<_> {
start,
finish,
next_fetch_block_ids,
config: _,
} = memory_interface;
sim.write(start.ready, false).await;
sim.write(finish.data, finish.ty().data.HdlNone()).await;
sim.write(next_fetch_block_ids, next_fetch_block_ids.ty().HdlNone())
.await;
sim.write(
queue_debug,
queue_debug.ty().new_sim(MemoryQueueEntry.default_sim()),
)
.await;
},
|sim, ()| run_fn(cd, memory_interface, queue_debug, &random, sim),
)
.await;
},
);
#[hdl]
async fn run_fn(
cd: Expr<ClockDomain>,
memory_interface: Expr<MemoryInterface<PhantomConst<CpuConfig>>>,
queue_debug: Expr<ArrayVec<MemoryQueueEntry, ConstUsize<{ MEMORY_QUEUE_SIZE }>>>,
random: &RefCell<Random>,
mut sim: ExternModuleSimulationState,
) {
let mut random = random.borrow_mut();
let config = memory_interface.config.ty();
let finish_data_ty = memory_interface.finish.data.ty();
let next_fetch_block_ids_ty = memory_interface.next_fetch_block_ids.ty();
let mut queue: VecDeque<SimValue<MemoryQueueEntry>> = VecDeque::new();
loop {
for entry in &mut queue {
entry.cycles_left = entry.cycles_left.as_int().saturating_sub(1).to_sim_value();
}
let sim_queue = queue_debug
.ty()
.from_iter_sim(MemoryQueueEntry.default_sim(), &queue)
.ok()
.expect("queue is known to be small enough");
sim.write(queue_debug, sim_queue).await;
sim.write_bool(
memory_interface.start.ready,
queue.len() < MEMORY_QUEUE_SIZE && random.next() % 32 != 0,
)
.await;
sim.write(
memory_interface.next_fetch_block_ids,
#[hdl(sim)]
next_fetch_block_ids_ty.HdlSome(
next_fetch_block_ids_ty
.HdlSome
.from_iter_sim(0u8, queue.iter().map(|entry| &entry.fetch_block_id))
.ok()
.expect("queue is known to be small enough"),
),
)
.await;
let finish_data = if let Some(entry) = queue
.front()
.filter(|entry| entry.cycles_left.as_int() == 0)
{
#[hdl(sim)]
let MemoryQueueEntry {
addr,
fetch_block_id: _,
cycles_left: _,
} = entry;
let addr = addr.as_int();
let mut read_data =
repeat(0u8, finish_data_ty.HdlSome.read_data.len()).to_sim_value();
let kind = if let Some(data) = read_memory(addr, read_data.len()) {
for (l, r) in read_data.iter_mut().zip(data) {
*l = r.to_sim_value();
}
#[hdl(sim)]
MemoryOperationFinishKind.Success(
#[hdl(sim)]
MemoryOperationKind.Read(),
)
} else {
#[hdl(sim)]
MemoryOperationFinishKind.Error(
#[hdl(sim)]
MemoryOperationErrorKind.Generic(),
)
};
#[hdl(sim)]
finish_data_ty.HdlSome(
#[hdl(sim)]
MemoryOperationFinish::<_> {
kind,
read_data,
config,
},
)
} else {
#[hdl(sim)]
finish_data_ty.HdlNone()
};
sim.write(memory_interface.finish.data, &finish_data).await;
sim.wait_for_clock_edge(cd.clk).await;
println!(
"Dump mock memory queue: {:#?}",
Vec::from_iter(queue.iter().map(|v| {
DebugAsDisplay(format!(
"fid={:#x} addr={:#x}",
v.fetch_block_id.as_int(),
v.addr.as_int(),
))
}))
);
if sim
.read_past_bool(memory_interface.start.ready, cd.clk)
.await
{
#[hdl(sim)]
if let HdlSome(memory_operation_start) =
sim.read_past(memory_interface.start.data, cd.clk).await
{
#[hdl(sim)]
let MemoryOperationStart::<_> {
kind,
addr,
write_data: _,
fetch_block_id,
config: _,
} = memory_operation_start;
#[hdl(sim)]
match kind {
MemoryOperationKind::Read => {}
MemoryOperationKind::Write => unreachable!(),
}
let entry = #[hdl(sim)]
MemoryQueueEntry {
addr,
fetch_block_id,
cycles_left: MemoryQueueEntry::get_next_delay(&mut random),
};
println!("mock memory start: {entry:#?}");
queue.push_back(entry);
}
}
if sim
.read_past_bool(memory_interface.finish.ready, cd.clk)
.await
{
#[hdl(sim)]
if let HdlSome(finish_data) = finish_data {
let Some(entry) = queue.pop_front() else {
unreachable!();
};
#[hdl(sim)]
let MemoryOperationFinish::<_> {
kind,
read_data,
config: _,
} = finish_data;
let kind = #[hdl(sim)]
match kind {
MemoryOperationFinishKind::Error(_) => Err(()),
MemoryOperationFinishKind::Success(_) => Ok(()),
};
println!(
"mock memory finish: kind={kind:?} read_data={read_data:?} {entry:#?}"
);
}
}
}
}
}
#[hdl_module]
fn dut(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let from_next_pc: NextPcToFetchInterface<PhantomConst<CpuConfig>> =
m.input(NextPcToFetchInterface[config]);
#[hdl]
let to_decode: FetchToDecodeInterface<PhantomConst<CpuConfig>> =
m.output(FetchToDecodeInterface[config]);
#[hdl]
let fetch = instance(fetch(config));
#[hdl]
let fetch {
cd: fetch_cd,
memory_interface: fetch_memory_interface,
from_next_pc: fetch_from_next_pc,
to_decode: fetch_to_decode,
} = fetch;
connect(fetch_cd, cd);
connect(fetch_from_next_pc, from_next_pc);
connect(to_decode, fetch_to_decode);
#[hdl]
let mock_memory = instance(mock_memory(config));
#[hdl]
let mock_memory {
cd: mock_memory_cd,
memory_interface: mock_memory_interface,
queue_debug: _,
} = mock_memory;
connect(mock_memory_cd, cd);
connect(mock_memory_interface, fetch_memory_interface);
}
#[derive(Clone)]
struct FetchTestOperation {
start_pc: u64,
fetch_block_id: u8,
fetch_block_data: [u8; FETCH_WIDTH_IN_BYTES],
error: Option<SimValue<MemoryOperationErrorKind>>,
}
impl PartialEq for FetchTestOperation {
#[hdl]
fn eq(&self, other: &Self) -> bool {
let Self {
start_pc,
fetch_block_id,
fetch_block_data,
ref error,
} = *self;
if let Some(error) = error {
#[hdl(sim)]
match error {
MemoryOperationErrorKind::Generic => {}
}
}
start_pc == other.start_pc
&& fetch_block_id == other.fetch_block_id
&& fetch_block_data == other.fetch_block_data
&& error.is_some() == other.error.is_some()
}
}
impl fmt::Debug for FetchTestOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
start_pc,
fetch_block_id,
fetch_block_data,
error,
} = self;
let mut debug_struct = f.debug_struct("FetchTestOperation");
debug_struct.field("start_pc", &format_args!("{start_pc:#x}"));
debug_struct.field("fetch_block_id", &format_args!("{fetch_block_id:#x}"));
if fetch_block_data.iter().all(|v| *v == fetch_block_data[0]) {
debug_struct.field(
"fetch_block_data",
&format_args!(
"[b'{}'; {FETCH_WIDTH_IN_BYTES}]",
fetch_block_data[0].escape_ascii(),
),
);
} else {
debug_struct.field(
"fetch_block_data",
&format_args!("b\"{}\"", fetch_block_data.escape_ascii()),
);
}
debug_struct.field("error", error).finish()
}
}
const LOG2_FETCH_WIDTH_IN_BYTES: u8 = 3;
const FETCH_WIDTH_IN_BYTES: usize = 1 << LOG2_FETCH_WIDTH_IN_BYTES;
const LOG2_CACHE_LINE_SIZE_IN_BYTES: u8 = 5;
const CACHE_LINE_SIZE_IN_BYTES: usize = 1 << LOG2_CACHE_LINE_SIZE_IN_BYTES;
// needs to be a multiple of the cache line size
const _: [(); CACHE_LINE_SIZE_IN_BYTES * 2] = [(); MEMORY_DATA.len()];
fn fetch_test_operations() -> Vec<FetchTestOperation> {
#[track_caller]
fn mem_data(r: std::ops::RangeFrom<usize>) -> [u8; FETCH_WIDTH_IN_BYTES] {
*MEMORY_DATA[r]
.as_bytes()
.first_chunk()
.expect("start should be in-range")
}
#[hdl]
fn generic_error() -> SimValue<MemoryOperationErrorKind> {
#[hdl(sim)]
MemoryOperationErrorKind.Generic()
}
let mut last_fetch_block_id = 0u8.wrapping_sub(1);
macro_rules! op {
{
$($field:ident: $value:expr,)*
} => {
FetchTestOperation {
fetch_block_id: {
last_fetch_block_id = last_fetch_block_id.wrapping_add(1);
last_fetch_block_id
},
$($field: $value,)*
}
};
}
vec![
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
op! {
start_pc: 0x1010,
fetch_block_data: mem_data(0x10..),
error: None,
},
op! {
start_pc: 0x1020,
fetch_block_data: mem_data(0x20..),
error: None,
},
op! {
start_pc: 0x100,
fetch_block_data: [0; _],
error: Some(generic_error()),
},
op! {
start_pc: MEMORY_ERROR_RANGE.start,
fetch_block_data: [0; _],
error: Some(generic_error()),
},
op! {
start_pc: MEMORY_ERROR_RANGE.start + MEMORY_ERROR_STEP,
fetch_block_data: [0; _],
error: Some(generic_error()),
},
op! {
start_pc: MEMORY_ERROR_RANGE.start + MEMORY_ERROR_STEP * 2,
fetch_block_data: [0; _],
error: Some(generic_error()),
},
op! {
start_pc: MEMORY_ERROR_RANGE.start + MEMORY_ERROR_STEP * 3,
fetch_block_data: [0; _],
error: Some(generic_error()),
},
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
op! {
start_pc: 0x1010,
fetch_block_data: mem_data(0x10..),
error: None,
},
op! {
start_pc: 0x1020,
fetch_block_data: mem_data(0x20..),
error: None,
},
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
op! {
start_pc: 0x1010,
fetch_block_data: mem_data(0x10..),
error: None,
},
op! {
start_pc: 0x1020,
fetch_block_data: mem_data(0x20..),
error: None,
},
op! {
start_pc: MEMORY_RANGE2.start,
fetch_block_data: [0xF2; _],
error: None,
},
op! {
start_pc: MEMORY_RANGE2.start,
fetch_block_data: [0xF2; _],
error: None,
},
op! {
start_pc: MEMORY_RANGE2.start,
fetch_block_data: [0xF2; _],
error: None,
},
op! {
start_pc: MEMORY_RANGE2.start,
fetch_block_data: [0xF2; _],
error: None,
},
op! {
start_pc: MEMORY_RANGE2.start,
fetch_block_data: [0xF2; _],
error: None,
},
op! {
start_pc: MEMORY_RANGE2.start,
fetch_block_data: [0xF2; _],
error: None,
},
op! {
start_pc: MEMORY_RANGE2.start,
fetch_block_data: [0xF2; _],
error: None,
},
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
op! {
start_pc: 0x1000,
fetch_block_data: mem_data(0..),
error: None,
},
]
}
#[test]
#[hdl]
fn test_fetch() {
let _n = SourceLocation::normalize_files_for_tests();
let mut config = CpuConfig::new(
vec![
UnitConfig::new(UnitKind::AluBranch),
UnitConfig::new(UnitKind::AluBranch),
],
NonZeroUsize::new(20).unwrap(),
);
config.fetch_width = NonZeroUsize::new(2).unwrap();
config.log2_fetch_width_in_bytes = LOG2_FETCH_WIDTH_IN_BYTES;
config.log2_cache_line_size_in_bytes = LOG2_CACHE_LINE_SIZE_IN_BYTES;
config.log2_l1_i_cache_line_count = 4;
let m = dut(PhantomConst::new_sized(config));
let mut sim = Simulation::new(m);
let writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
struct DumpVcdOnDrop {
writer: Option<RcWriter>,
}
impl Drop for DumpVcdOnDrop {
fn drop(&mut self) {
if let Some(mut writer) = self.writer.take() {
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
}
}
}
let mut writer = DumpVcdOnDrop {
writer: Some(writer),
};
let from_next_pc_ty = sim.io().from_next_pc.ty();
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
sim.write(
sim.io().from_next_pc.cancel.data,
from_next_pc_ty.cancel.data.HdlNone(),
);
sim.write(
sim.io().from_next_pc.fetch.data,
from_next_pc_ty.fetch.data.HdlNone(),
);
sim.write(sim.io().to_decode.fetched.ready, true);
sim.write(
sim.io().to_decode.next_fetch_block_ids,
HdlSome(
sim.io()
.ty()
.to_decode
.next_fetch_block_ids
.HdlSome
.new_sim(0u8),
),
);
let operations = fetch_test_operations();
let mut started_operations = 0;
let mut finished_operations = 0;
for cycle in 0..150 {
sim.write(
sim.io().from_next_pc.fetch.data,
if let Some(op) = operations.get(started_operations) {
#[hdl(sim)]
HdlSome(
#[hdl(sim)]
NextPcToFetchInterfaceInner {
start_pc: op.start_pc,
fetch_block_id: op.fetch_block_id,
},
)
} else {
#[hdl(sim)]
HdlNone()
},
);
sim.advance_time(SimDuration::from_nanos(500));
#[hdl(sim)]
if let HdlSome(next_fetch_block_ids) = sim.read(sim.io().from_next_pc.next_fetch_block_ids)
{
let next_fetch_block_ids = ArrayVec::elements_sim_ref(&next_fetch_block_ids);
let expected_next_fetch_block_ids = Vec::from_iter(
operations
.get(finished_operations..started_operations)
.unwrap_or(&[])
.iter()
.map(|op| op.fetch_block_id.to_sim_value()),
);
println!("expected_next_fetch_block_ids={expected_next_fetch_block_ids:?}");
assert_eq!(next_fetch_block_ids, expected_next_fetch_block_ids);
}
if sim.read_bool(sim.io().from_next_pc.fetch.ready) {
#[hdl(sim)]
if let HdlSome(_) = sim.read(sim.io().from_next_pc.fetch.data) {
println!("started fetch: {:#?}", operations[started_operations]);
started_operations += 1;
}
} else {
println!("not ready to start fetch");
}
if sim.read_bool(sim.io().to_decode.fetched.ready) {
#[hdl(sim)]
if let HdlSome(fetched) = sim.read(sim.io().to_decode.fetched.data) {
#[hdl(sim)]
let FetchToDecodeInterfaceInner::<_> {
start_pc,
fetch_block_id,
fetch_block_data,
error,
config: _,
} = &fetched;
let Some(expected_op) = operations.get(finished_operations) else {
panic!("too many finished operations: {fetched:#?}");
};
let op = FetchTestOperation {
start_pc: start_pc.as_int(),
fetch_block_id: fetch_block_id.as_int(),
error: #[hdl(sim)]
match error {
HdlSome(e) => Some(e.clone()),
HdlNone => None,
},
fetch_block_data: std::array::from_fn(|i| fetch_block_data[i].as_int()),
};
println!("finished fetch: op={op:#?}");
assert_eq!(
op, *expected_op,
"cycle={cycle} finished_operations={finished_operations}",
);
finished_operations += 1;
} else {
println!("not ready to finish fetch");
}
}
println!("clock tick: {cycle}");
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_nanos(500));
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, false);
}
assert_eq!(finished_operations, operations.len());
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("expected/fetch.vcd") {
panic!();
}
}

View file

@ -0,0 +1,92 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use cpu::{
config::{CpuConfig, UnitConfig},
instruction::{AddSubMOp, LogicalMOp, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode},
main_memory::main_memory,
reg_alloc::{FetchedDecodedMOp, reg_alloc},
register::{FlagsMode, PRegFlagsPowerISA},
unit::{GlobalState, UnitKind},
};
use fayalite::{
assert_export_firrtl,
firrtl::ExportOptions,
prelude::*,
sim::{Simulation, time::SimDuration, vcd::VcdWriterDecls},
util::RcWriter,
};
use std::num::NonZeroUsize;
//new test - much simpler
#[test]
fn test_main_memory() {
let mut config = CpuConfig::new(
vec![
UnitConfig::new(UnitKind::AluBranch),
UnitConfig::new(UnitKind::AluBranch),
],
NonZeroUsize::new(20).unwrap(),
);
// create a simulation from main_memory()
let mut sim = Simulation::new(main_memory(&config));
// add a .vcd writer that writes to main_memory.vcd -- this is simple for demo purposes,
// but for our actual code we should do better than just writing
// to main_memory.vcd in the repository's root
//WRONG: sim.add_trace_writer(std::fs::File::create("main_memory.vcd").unwrap());
let out_file = std::fs::File::create("main_memory.vcd").unwrap();
sim.add_trace_writer(VcdWriterDecls::new(out_file));
sim.write(sim.io().en, true);
sim.write(sim.io().cd.rst, false);
sim.write(sim.io().cd.clk, false);
sim.write(sim.io().write_en, false);
sim.write(sim.io().write_data, 0xFF00FF00FF00FF00u64);
// TODO convert to for loop
// you need to write an initial value to all inputs before you can start running the simulation
sim.write(sim.io().addr, 0x12345u64);
// now wait 1us because why not
sim.advance_time(SimDuration::from_micros(1)); //panic here at simulation
dbg!(sim.read(sim.io().read_data)); // dbg! macro just displays the value you pass to it
for n in 0u64..4u64 {
sim.write(sim.io().addr, n);
// now wait 1us because why not
sim.advance_time(SimDuration::from_micros(1));
}
sim.write(sim.io().write_en, true);
sim.write(sim.io().addr, 0u64);
sim.write(sim.io().write_data, 0xFFFFFFFFFFFFFFFFu64); //fill with ones
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().cd.clk, false);
sim.advance_time(SimDuration::from_micros(1));
sim.write(sim.io().addr, 1u64);
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().cd.clk, false);
sim.advance_time(SimDuration::from_micros(1));
sim.write(sim.io().addr, 2u64);
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().cd.clk, false);
sim.advance_time(SimDuration::from_micros(1));
sim.write(sim.io().addr, 3u64);
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().cd.clk, false);
sim.advance_time(SimDuration::from_micros(1));
sim.flush_traces().unwrap(); // make sure everything is written to the output file
}

File diff suppressed because it is too large Load diff

View file

@ -3,12 +3,14 @@
use cpu::{ use cpu::{
config::{CpuConfig, UnitConfig}, config::{CpuConfig, UnitConfig},
instruction::{AddSubMOp, LogicalMOp, Lut4, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode}, instruction::{AddSubMOp, LogicalMOp, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode},
reg_alloc::{FetchedDecodedMOp, reg_alloc}, reg_alloc::{FetchedDecodedMOp, reg_alloc},
register::{FlagsMode, PRegFlagsPowerISA}, register::{FlagsMode, PRegFlagsPowerISA},
unit::{GlobalState, UnitKind}, unit::{GlobalState, UnitKind},
}; };
use fayalite::{ use fayalite::{
assert_export_firrtl,
firrtl::ExportOptions,
prelude::*, prelude::*,
sim::{Simulation, time::SimDuration, vcd::VcdWriterDecls}, sim::{Simulation, time::SimDuration, vcd::VcdWriterDecls},
util::RcWriter, util::RcWriter,
@ -59,7 +61,7 @@ fn test_reg_alloc() {
[HdlSome(()), HdlNone()], [HdlSome(()), HdlNone()],
}, },
[0u8; 2], [0u8; 2],
0x12345678u32.cast_to_static::<SInt<_>>(), 0x12345678u32.cast_to_static(),
OutputIntegerMode.DupLow32(), OutputIntegerMode.DupLow32(),
false, false,
false, false,
@ -81,7 +83,7 @@ fn test_reg_alloc() {
[HdlSome(()), HdlNone()], [HdlSome(()), HdlNone()],
}, },
[1u8, 0, 0], [1u8, 0, 0],
1.cast_to_static::<SInt<_>>(), 1.cast_to_static(),
OutputIntegerMode.Full64(), OutputIntegerMode.Full64(),
false, false,
false, false,
@ -99,9 +101,9 @@ fn test_reg_alloc() {
flag_regs: [HdlNone(), HdlSome(())], flag_regs: [HdlNone(), HdlSome(())],
}, },
[2u8, 4u8], [2u8, 4u8],
0.cast_to_static::<SInt<_>>(), 0.cast_to_static(),
OutputIntegerMode.Full64(), OutputIntegerMode.Full64(),
Lut4::from_fn(|a, b| a ^ b), 0b0110_hdl_u4,
), ),
]; ];
let insns = insns_init.into_iter().chain(insns_loop.into_iter().cycle()); let insns = insns_init.into_iter().chain(insns_loop.into_iter().cycle());

View file

@ -1,208 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::TestCase;
use cpu::{
decoder::simple_power_isa::decode_one_insn, instruction::MOp, util::array_vec::ArrayVec,
};
use fayalite::{prelude::*, sim::vcd::VcdWriterDecls, util::RcWriter};
use std::{fmt::Write as _, io::Write, process::Command};
mod test_cases;
#[test]
fn test_test_cases_assembly() -> std::io::Result<()> {
let llvm_mc_regex = regex::Regex::new(r"llvm-mc(-\d+)?$").expect("known to be a valid regex");
let llvm_mc = which::which_re(llvm_mc_regex)
.expect("can't find llvm-mc or llvm-mc-<num> in path")
.next()
.expect("can't find llvm-mc or llvm-mc-<num> in path");
let test_cases = test_cases::test_cases();
let mut assembly = String::new();
for TestCase {
mnemonic,
first_input: _,
second_input: _,
output: _,
loc: _,
} in &test_cases
{
writeln!(assembly, "{mnemonic}").unwrap();
}
let (reader, mut writer) = std::io::pipe()?;
let thread = std::thread::spawn(move || writer.write_all(assembly.as_bytes()));
let std::process::Output {
status,
stdout,
stderr,
} = Command::new(&llvm_mc)
.arg("--triple=powerpc64le-linux-gnu")
.arg("--assemble")
.arg("--filetype=asm")
.arg("--show-encoding")
.arg("-")
.stdin(reader)
.output()?;
let _ = thread.join();
let stderr = String::from_utf8_lossy(&stderr);
eprint!("{stderr}");
if !status.success() {
panic!("{} failed: {status}", llvm_mc.display());
}
let stdout = String::from_utf8_lossy(&stdout);
print!("{stdout}");
let mut lines = stdout.lines();
let text_line = lines.next();
assert_eq!(text_line, Some("\t.text"));
let mut any_error = false;
macro_rules! assert_eq_cont {
($l:expr, $r:expr, $($msg:tt)+) => {
match (&$l, &$r) {
(l, r) => if l != r {
eprintln!("assertion failed: {}\nl={l:#?}\nr={r:#?}", format_args!($($msg)+));
any_error = true;
}
}
};
}
for test_case @ TestCase {
mnemonic: _,
first_input,
second_input,
output: _,
loc: _,
} in test_cases
{
let Some(line) = lines.next() else {
panic!("output missing line for: {test_case:?}");
};
if line.starts_with("\t.long") {
assert_eq!(
line,
format!("\t.long\t{first_input}"),
"test_case={test_case:?}\nline:\n{line}"
);
if let Some(second_input) = second_input {
let Some(line) = lines.next() else {
panic!("output missing line for: {test_case:?}");
};
assert_eq!(
line,
format!("\t.long\t{second_input}"),
"test_case={test_case:?}\nline:\n{line}"
);
}
continue;
}
let Some((_, comment)) = line.split_once('#') else {
panic!("output line missing comment. test_case={test_case:?}\nline:\n{line}");
};
let [b0, b1, b2, b3] = first_input.to_le_bytes();
let expected_comment = if let Some(second_input) = second_input {
let [b4, b5, b6, b7] = second_input.to_le_bytes();
format!(
" encoding: [0x{b0:02x},0x{b1:02x},0x{b2:02x},0x{b3:02x},0x{b4:02x},0x{b5:02x},0x{b6:02x},0x{b7:02x}]"
)
} else {
format!(" encoding: [0x{b0:02x},0x{b1:02x},0x{b2:02x},0x{b3:02x}]")
};
assert_eq_cont!(
comment,
expected_comment,
"test_case={test_case:?}\nline:\n{line}"
);
}
for line in lines {
assert!(line.trim().is_empty(), "bad trailing output line: {line:?}");
}
if any_error {
panic!();
}
Ok(())
}
#[hdl]
#[test]
fn test_decode_insn() {
let _n = SourceLocation::normalize_files_for_tests();
let m = decode_one_insn();
let mut sim = Simulation::new(m);
let writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
struct DumpVcdOnDrop {
writer: Option<RcWriter>,
}
impl Drop for DumpVcdOnDrop {
fn drop(&mut self) {
if let Some(mut writer) = self.writer.take() {
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
}
}
}
let mut writer = DumpVcdOnDrop {
writer: Some(writer),
};
for test_case @ TestCase {
mnemonic: _,
first_input,
second_input,
output: _,
loc: _,
} in test_cases::test_cases()
{
sim.write(sim.io().first_input, first_input);
sim.write(
sim.io().second_input,
if let Some(v) = second_input {
#[hdl(sim)]
HdlSome(v)
} else {
#[hdl(sim)]
HdlNone()
},
);
sim.advance_time(SimDuration::from_micros(1));
let second_input_used = sim.read_bool(sim.io().second_input_used);
let is_illegal = sim.read_bool(sim.io().is_illegal);
let output = sim.read(sim.io().output);
#[derive(Debug)]
#[expect(dead_code, reason = "used only for Debug formatting")]
struct FormattedOutput<'a> {
insns: &'a [SimValue<MOp>],
second_input_used: bool,
is_illegal: bool,
}
let expected = format!(
"{:#?}",
FormattedOutput {
insns: ArrayVec::elements_sim_ref(&test_case.output),
second_input_used: second_input.is_some(),
is_illegal: false,
},
);
let output = format!(
"{:#?}",
FormattedOutput {
insns: ArrayVec::elements_sim_ref(&output),
second_input_used,
is_illegal,
},
);
assert!(
expected == output,
"test_case={test_case:#?}\noutput={output}\nexpected={expected}"
);
}
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("expected/decode_one_insn.vcd") {
panic!();
}
}
#[hdl]
#[test]
fn test_simple_power_isa_decoder() {
// TODO
}

View file

@ -1,158 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use cpu::{instruction::MOp, util::array_vec::ArrayVec};
use fayalite::prelude::*;
use std::fmt;
mod branch;
mod condition_register;
mod fixed_point_arithmetic;
mod fixed_point_compare;
mod fixed_point_load;
mod fixed_point_logical;
mod fixed_point_rotate_and_shift;
mod fixed_point_store;
mod move_to_from_system_register;
mod prefixed_no_operation;
pub struct TestCase {
pub mnemonic: &'static str,
pub first_input: u32,
pub second_input: Option<u32>,
pub output: SimValue<ArrayVec<MOp, ConstUsize<3>>>,
pub loc: &'static std::panic::Location<'static>,
}
impl fmt::Debug for TestCase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
mnemonic,
first_input,
second_input,
output,
loc,
} = self;
let mut debug_struct = f.debug_struct("TestCase");
debug_struct
.field("mnemonic", mnemonic)
.field("first_input", &format_args!("0x{first_input:08x}"));
if let Some(second_input) = second_input {
debug_struct.field("second_input", &format_args!("0x{second_input:08x}"));
} else {
debug_struct.field("second_input", &format_args!("None"));
}
debug_struct
.field("output", &ArrayVec::elements_sim_ref(output))
.field("loc", &format_args!("{loc}"))
.finish()
}
}
#[track_caller]
fn insn_empty(mnemonic: &'static str, first_input: u32, second_input: Option<u32>) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
TestCase {
mnemonic,
first_input,
second_input,
output: ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop),
loc: std::panic::Location::caller(),
}
}
#[track_caller]
fn insn_single(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
output: impl ToSimValue<Type = MOp>,
) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop);
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
ArrayVec::elements_sim_mut(&mut single_storage)[0] = output.to_sim_value();
TestCase {
mnemonic,
first_input,
second_input,
output: single_storage,
loc: std::panic::Location::caller(),
}
}
#[track_caller]
fn insn_double(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
insns: [impl ToSimValue<Type = MOp>; 2],
) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop);
ArrayVec::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space");
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
ArrayVec::elements_sim_mut(&mut single_storage)[0] = insns[0].to_sim_value();
ArrayVec::elements_sim_mut(&mut single_storage)[1] = insns[1].to_sim_value();
TestCase {
mnemonic,
first_input,
second_input,
output: single_storage,
loc: std::panic::Location::caller(),
}
}
#[track_caller]
fn insn_triple(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
insns: [impl ToSimValue<Type = MOp>; 3],
) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop);
ArrayVec::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space");
ArrayVec::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space");
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
ArrayVec::elements_sim_mut(&mut single_storage)[0] = insns[0].to_sim_value();
ArrayVec::elements_sim_mut(&mut single_storage)[1] = insns[1].to_sim_value();
ArrayVec::elements_sim_mut(&mut single_storage)[2] = insns[2].to_sim_value();
TestCase {
mnemonic,
first_input,
second_input,
output: single_storage,
loc: std::panic::Location::caller(),
}
}
pub fn test_cases() -> Vec<TestCase> {
let mut retval = Vec::new();
branch::test_cases_book_i_2_4_branch(&mut retval);
condition_register::test_cases_book_i_2_5_condition_register(&mut retval);
fixed_point_load::test_cases_book_i_3_3_2_fixed_point_load(&mut retval);
fixed_point_store::test_cases_book_i_3_3_3_fixed_point_store(&mut retval);
fixed_point_arithmetic::test_cases_book_i_3_3_9_fixed_point_arithmetic(&mut retval);
fixed_point_compare::test_cases_book_i_3_3_10_fixed_point_compare(&mut retval);
fixed_point_logical::test_cases_book_i_3_3_13_fixed_point_logical(&mut retval);
fixed_point_rotate_and_shift::test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(
&mut retval,
);
move_to_from_system_register::test_cases_book_i_3_3_19_move_to_from_system_register(
&mut retval,
);
prefixed_no_operation::test_cases_book_i_3_3_20_prefixed_no_operation(&mut retval);
move_to_from_system_register::test_cases_book_iii_5_4_4_move_to_from_system_register(
&mut retval,
);
retval
}

View file

@ -1,446 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_double, insn_single};
use cpu::instruction::{
AddSubMOp, BranchMOp, ConditionMode, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode,
};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 2.4 Branch Instructions
pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
retval.push(insn_single(
"b 0x345678",
0x48345678,
None,
BranchMOp::branch_i(
MOpDestReg::new_sim(&[], &[]),
MOpRegNum::const_zero().value,
0x345678.cast_to_static::<SInt<_>>(),
true,
false,
false,
),
));
retval.push(insn_single(
"ba 0x345678",
0x4834567a,
None,
BranchMOp::branch_i(
MOpDestReg::new_sim(&[], &[]),
MOpRegNum::const_zero().value,
0x345678.cast_to_static::<SInt<_>>(),
false,
false,
false,
),
));
retval.push(insn_single(
"bl 0x345678",
0x48345679,
None,
BranchMOp::branch_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
MOpRegNum::const_zero().value,
0x345678.cast_to_static::<SInt<_>>(),
true,
true,
false,
),
));
retval.push(insn_single(
"bla 0x345678",
0x4834567b,
None,
BranchMOp::branch_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
MOpRegNum::const_zero().value,
0x345678.cast_to_static::<SInt<_>>(),
false,
true,
false,
),
));
fn insn_dec_ctr_and(
mnemonic: &'static str,
first_input: u32,
second_input: Option<u32>,
second_insn: impl ToSimValue<Type = MOp>,
) -> TestCase {
insn_double(
mnemonic,
first_input,
second_input,
[
AddSubMOp::add_sub_i::<MOp>(
MOpDestReg::new([MOpRegNum::power_isa_ctr_reg()], []),
[
MOpRegNum::power_isa_ctr_reg().value,
MOpRegNum::const_zero().value,
],
(-1).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
)
.into_sim_value(),
second_insn.into_sim_value(),
],
)
}
macro_rules! insn_branch_conds {
(
mnemonic = $mnemonic:literal;
mnemonic_l = $mnemonic_l:literal;
asm_last_arg = $asm_last_arg:literal;
imm = $imm:literal;
encoding = $encoding:literal;
src1 = $src1:expr;
pc_relative = $pc_relative:expr;
is_ret = $is_ret:expr;
) => {
insn_branch_conds! {
mnemonic = $mnemonic;
asm_last_arg = $asm_last_arg;
imm = $imm;
encoding = $encoding;
dest = MOpDestReg::new_sim(&[], &[]);
src1 = $src1;
pc_relative = $pc_relative;
lk = false;
is_ret = $is_ret;
}
insn_branch_conds! {
mnemonic = $mnemonic_l;
asm_last_arg = $asm_last_arg;
imm = $imm;
encoding = $encoding | 1;
dest = MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]);
src1 = $src1;
pc_relative = $pc_relative;
lk = true;
is_ret = $is_ret;
}
};
(
mnemonic = $mnemonic:literal;
asm_last_arg = $asm_last_arg:literal;
imm = $imm:literal;
encoding = $encoding:expr;
dest = $dest:expr;
src1 = $src1:expr;
pc_relative = $pc_relative:expr;
lk = $lk:expr;
is_ret = $is_ret:expr;
) => {
if !$mnemonic.starts_with("bcctr") {
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 0, ", $asm_last_arg),
$encoding,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 1, ", $asm_last_arg),
$encoding | 0x010000,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SGt(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 2, ", $asm_last_arg),
$encoding | 0x020000,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.Eq(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 3, ", $asm_last_arg),
$encoding | 0x030000,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.Overflow(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 0, 9, ", $asm_last_arg),
$encoding | 0x090000,
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(2).value,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SGt(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 2, 0, ", $asm_last_arg),
$encoding | (2 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
false,
$pc_relative,
$lk,
$is_ret,
),
));
}
retval.push(insn_single(
concat!($mnemonic, " 4, 0, ", $asm_last_arg),
$encoding | (4 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::const_zero().value,
],
$imm.cast_to_static::<SInt<_>>(),
true,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
if !$mnemonic.starts_with("bcctr") {
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 8, 0, ", $asm_last_arg),
$encoding | (8 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 10, 0, ", $asm_last_arg),
$encoding | (10 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
false,
$pc_relative,
$lk,
$is_ret,
),
));
}
retval.push(insn_single(
concat!($mnemonic, " 12, 0, ", $asm_last_arg),
$encoding | (12 << 21),
None,
BranchMOp::branch_cond_ctr(
$dest,
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
$src1,
MOpRegNum::const_zero().value,
],
$imm.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SLt(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
if !$mnemonic.starts_with("bcctr") {
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 16, 0, ", $asm_last_arg),
$encoding | (16 << 21),
None,
BranchMOp::branch_ctr(
$dest,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
$imm.cast_to_static::<SInt<_>>(),
true,
$pc_relative,
$lk,
$is_ret,
),
));
retval.push(insn_dec_ctr_and(
concat!($mnemonic, " 18, 0, ", $asm_last_arg),
$encoding | (18 << 21),
None,
BranchMOp::branch_ctr(
$dest,
$src1,
MOpRegNum::power_isa_ctr_reg().value,
$imm.cast_to_static::<SInt<_>>(),
false,
$pc_relative,
$lk,
$is_ret,
),
));
}
retval.push(insn_single(
concat!($mnemonic, " 20, 0, ", $asm_last_arg),
$encoding | (20 << 21),
None,
BranchMOp::branch_i(
$dest,
$src1,
$imm.cast_to_static::<SInt<_>>(),
$pc_relative,
$lk,
$is_ret,
),
));
};
}
insn_branch_conds! {
mnemonic = "bc";
mnemonic_l = "bcl";
asm_last_arg = "0x1234";
imm = 0x1234;
encoding = 0x40001234;
src1 = MOpRegNum::const_zero().value;
pc_relative = true;
is_ret = false;
}
insn_branch_conds! {
mnemonic = "bca";
mnemonic_l = "bcla";
asm_last_arg = "0x1234";
imm = 0x1234;
encoding = 0x40001236;
src1 = MOpRegNum::const_zero().value;
pc_relative = false;
is_ret = false;
}
insn_branch_conds! {
mnemonic = "bclr";
mnemonic_l = "bclrl";
asm_last_arg = "0";
imm = 0;
encoding = 0x4c000020;
src1 = MOpRegNum::power_isa_lr_reg().value;
pc_relative = false;
is_ret = true;
}
insn_branch_conds! {
mnemonic = "bcctr";
mnemonic_l = "bcctrl";
asm_last_arg = "0";
imm = 0;
encoding = 0x4c000420;
src1 = MOpRegNum::power_isa_ctr_reg().value;
pc_relative = false;
is_ret = false;
}
retval.push(insn_dec_ctr_and(
// LLVM doesn't support the bctar[l] instructions:
// https://github.com/llvm/llvm-project/issues/176864
".long 0x4e400461 # bctarl 18, 0, 0",
0x4e400461,
None,
BranchMOp::branch_ctr(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
MOpRegNum::power_isa_tar_reg().value,
MOpRegNum::power_isa_ctr_reg().value,
0.cast_to_static::<SInt<_>>(),
false,
false,
true,
false,
),
));
}

View file

@ -1,103 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_single};
use cpu::{
instruction::{LogicalFlagsMOp, LogicalFlagsMOpImm, Lut4, MOpDestReg, MOpRegNum, MoveRegMOp},
register::PRegFlagsPowerISA,
};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 2.5 Condition Register Instructions
pub fn test_cases_book_i_2_5_condition_register(retval: &mut Vec<TestCase>) {
macro_rules! cr_bit_logical_op {
(
$mnemonic:literal,
$encoding:literal,
$lut:expr
) => {{
retval.push(insn_single(
concat!($mnemonic, " 4*cr3+so, 4*cr1+gt, 4*cr5+lt"),
$encoding | 0x01e5a000,
None,
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(1).value,
MOpRegNum::power_isa_cr_reg_imm(5).value,
MOpRegNum::power_isa_cr_reg_imm(3).value,
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.so = Some((src0.cr_gt, src1.cr_lt).into());
dest
}),
$lut,
),
));
retval.push(insn_single(
concat!($mnemonic, " lt, gt, eq"),
$encoding | 0x00011000,
None,
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(0)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
MOpRegNum::power_isa_cr_reg_imm(0).value,
MOpRegNum::power_isa_cr_reg_imm(0).value,
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.cr_lt = Some((src0.cr_gt, src1.cr_eq).into());
dest
}),
$lut,
),
));
retval.push(insn_single(
concat!($mnemonic, " gt, gt, eq"),
$encoding | 0x00211000,
None,
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(0)], &[]),
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
MOpRegNum::power_isa_cr_reg_imm(0).value,
MOpRegNum::power_isa_cr_reg_imm(0).value,
],
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
let mut dest = src2.map(|v| Some(v.into()));
dest.cr_gt = Some((src0.cr_gt, src1.cr_eq).into());
dest
}),
$lut,
),
));
}};
}
cr_bit_logical_op!("crand", 0x4c000202, Lut4::from_fn(|a, b| a & b));
cr_bit_logical_op!("crnand", 0x4c0001c2, Lut4::from_fn(|a, b| !(a & b)));
cr_bit_logical_op!("cror", 0x4c000382, Lut4::from_fn(|a, b| a | b));
cr_bit_logical_op!("crxor", 0x4c000182, Lut4::from_fn(|a, b| a ^ b));
cr_bit_logical_op!("crnor", 0x4c000042, Lut4::from_fn(|a, b| !(a | b)));
cr_bit_logical_op!("creqv", 0x4c000242, Lut4::from_fn(|a, b| a == b));
cr_bit_logical_op!("crandc", 0x4c000102, Lut4::from_fn(|a, b| a & !b));
cr_bit_logical_op!("crorc", 0x4c000342, Lut4::from_fn(|a, b| a | !b));
macro_rules! mcrf {
($dest:literal, $src:literal; $encoding:literal) => {
retval.push(insn_single(
concat!("mcrf ", $dest, ", ", $src),
$encoding,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_cr_reg_imm($src).value],
0i8.cast_to_static::<SInt<_>>(),
),
));
};
}
mcrf!(0, 0; 0x4c000000);
mcrf!(5, 7; 0x4e9c0000);
mcrf!(5, 0; 0x4e800000);
}

View file

@ -1,408 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_single};
use cpu::instruction::{AddSubMOp, MOpDestReg, MOpRegNum, OutputIntegerMode};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.9 Fixed-Point Arithmetic Instructions
pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>) {
retval.push(insn_single(
"addi 3, 4, 0x1234",
0x38641234,
None,
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::const_zero().value,
],
0x1234.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
));
retval.push(insn_single(
"paddi 3, 4, 0x123456789, 0",
0x06012345,
Some(0x38646789),
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::const_zero().value,
],
0x123456789i64.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
));
retval.push(insn_single(
"paddi 3, 0, 0x123456789, 1",
0x06112345,
Some(0x38606789),
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value, MOpRegNum::const_zero().value],
0x123456789i64.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
true,
),
));
retval.push(insn_single(
"addis 3, 4, 0x1234",
0x3C641234,
None,
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::const_zero().value,
],
0x12340000.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
));
retval.push(insn_single(
"addpcis 3, 0x1234",
0x4c7a1204,
None,
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; _],
0x12340004.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
true,
),
));
retval.push(insn_single(
"add. 3, 4, 5",
0x7c642a15,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num(3)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
MOpRegNum::const_zero().value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
));
retval.push(insn_single(
"addic. 3, 4, 0x1234",
0x34641234,
None,
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::const_zero().value,
],
0x1234.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
));
retval.push(insn_single(
"subf. 3, 4, 5",
0x7c642851,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num(3)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
MOpRegNum::const_zero().value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
true,
false,
true,
false,
),
));
retval.push(insn_single(
"subfic 3, 4, 0x1234",
0x20641234,
None,
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::const_zero().value,
],
0x1234.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
true,
false,
true,
false,
),
));
retval.push(insn_single(
"addc. 3, 4, 5",
0x7c642815,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
MOpRegNum::const_zero().value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
));
retval.push(insn_single(
"subfc. 3, 4, 5",
0x7c642811,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
MOpRegNum::const_zero().value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
true,
false,
true,
false,
),
));
retval.push(insn_single(
"adde. 3, 4, 5",
0x7c642915,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
true,
false,
false,
),
));
retval.push(insn_single(
"subfe. 3, 4, 5",
0x7c642911,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
true,
true,
false,
false,
),
));
retval.push(insn_single(
"addme. 3, 4",
0x7c6401d5,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::const_zero().value,
],
(-1i8).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
true,
false,
false,
),
));
retval.push(insn_single(
"subfme. 3, 4",
0x7c6401d1,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::const_zero().value,
],
(-1i8).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
true,
true,
false,
false,
),
));
retval.push(insn_single(
"addze. 3, 4",
0x7c640195,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::const_zero().value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
true,
false,
false,
),
));
retval.push(insn_single(
"subfze. 3, 4",
0x7c640191,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[
MOpRegNum::power_isa_gpr_reg_num(3),
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::const_zero().value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
true,
true,
false,
false,
),
));
retval.push(insn_single(
"neg. 3, 4",
0x7c6400d1,
None,
AddSubMOp::add_sub(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num(3)],
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::const_zero().value,
MOpRegNum::const_zero().value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
true,
false,
true,
false,
),
));
}

View file

@ -1,163 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_single};
use cpu::instruction::{CompareMOp, CompareMode, MOpDestReg, MOpRegNum, OutputIntegerMode};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.10 Fixed-Point Compare Instructions
pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>) {
retval.push(insn_single(
"cmpi 3, 0, 4, 0x1234",
0x2d841234,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4).value],
0x1234.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.S32(),
),
));
retval.push(insn_single(
"cmpi 3, 1, 4, -0x7655",
0x2da489ab,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4).value],
(0x89abu16 as i16).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.S64(),
),
));
retval.push(insn_single(
"cmp 3, 0, 4, 5",
0x7d842800,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.S32(),
),
));
retval.push(insn_single(
"cmp 3, 1, 4, 5",
0x7da42800,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.S64(),
),
));
retval.push(insn_single(
"cmpli 3, 0, 4, 0x1234",
0x29841234,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4).value],
0x1234.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.U32(),
),
));
retval.push(insn_single(
"cmpli 3, 1, 4, 0x89ab",
0x29a489ab,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4).value],
0x89ab.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.U64(),
),
));
retval.push(insn_single(
"cmpl 3, 0, 4, 5",
0x7d842840,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.U32(),
),
));
retval.push(insn_single(
"cmpl 3, 1, 4, 5",
0x7da42840,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.U64(),
),
));
retval.push(insn_single(
"cmprb 3, 0, 4, 5",
0x7d842980,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.CmpRBOne(),
),
));
retval.push(insn_single(
"cmprb 3, 1, 4, 5",
0x7da42980,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.CmpRBTwo(),
),
));
retval.push(insn_single(
"cmpeqb 3, 4, 5",
0x7d8429c0,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(4).value,
MOpRegNum::power_isa_gpr_reg_imm(5).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.CmpEqB(),
),
));
}

View file

@ -1,525 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_double};
use cpu::instruction::{
AddSubMOp, LoadMOp, LoadStoreConversion, LoadStoreWidth, MOpDestReg, MOpRegNum,
OutputIntegerMode,
};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.2 Fixed-Point Load Instructions
pub fn test_cases_book_i_3_3_2_fixed_point_load(retval: &mut Vec<TestCase>) {
macro_rules! load_prefixed {
(
$mnemonic:literal $dest:literal, $disp:literal($ra:literal), $r:literal;
$prefix:literal, $suffix:literal;
$width:ident;
$conversion:ident;
) => {
retval.push(insn_double(
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, "), ", $r),
$prefix,
Some($suffix),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $r != 0 || $ra == 0 {
MOpRegNum::const_zero().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
$r != 0,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg().value],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
));
};
}
macro_rules! load {
(
$mnemonic:literal $dest:literal, $disp:literal($ra:literal);
$encoding:literal;
$width:ident;
$conversion:ident;
) => {
retval.push(insn_double(
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg().value],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
));
};
}
macro_rules! load_update {
(
$mnemonic:literal $dest:literal, $disp:literal($ra:literal);
$encoding:literal;
$width:ident;
$conversion:ident;
) => {
retval.push(insn_double(
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm($ra).value],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
));
};
}
macro_rules! load_indexed {
(
$mnemonic:literal $dest:literal, $ra:literal, $rb:literal;
$encoding:literal;
$width:ident;
$conversion:ident;
) => {
retval.push(insn_double(
concat!($mnemonic, " ", $dest, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_temp_reg().value],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
));
};
}
macro_rules! load_update_indexed {
(
$mnemonic:literal $dest:literal, $ra:literal, $rb:literal;
$encoding:literal;
$width:ident;
$conversion:ident;
) => {
retval.push(insn_double(
concat!($mnemonic, " ", $dest, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm($ra).value],
LoadStoreWidth.$width(),
LoadStoreConversion.$conversion(),
),
],
));
};
}
load! {
"lbz" 3, 0x1234(4);
0x88641234;
Width8Bit;
ZeroExt;
}
load! {
"lbz" 3, 0x1234(0);
0x88601234;
Width8Bit;
ZeroExt;
}
load_prefixed! {
"plbz" 3, 0x123456789(4), 0;
0x06012345, 0x88646789;
Width8Bit;
ZeroExt;
}
load_prefixed! {
"plbz" 3, 0x123456789(0), 0;
0x06012345, 0x88606789;
Width8Bit;
ZeroExt;
}
load_prefixed! {
"plbz" 3, 0x123456789(0), 1;
0x06112345, 0x88606789;
Width8Bit;
ZeroExt;
}
load_indexed! {
"lbzx" 3, 4, 5;
0x7c6428ae;
Width8Bit;
ZeroExt;
}
load_indexed! {
"lbzx" 3, 0, 5;
0x7c6028ae;
Width8Bit;
ZeroExt;
}
load_update! {
"lbzu" 3, 0x1234(4);
0x8c641234;
Width8Bit;
ZeroExt;
}
load_update_indexed! {
"lbzux" 3, 4, 5;
0x7c6428ee;
Width8Bit;
ZeroExt;
}
load! {
"lhz" 3, 0x1234(4);
0xa0641234;
Width16Bit;
ZeroExt;
}
load! {
"lhz" 3, 0x1234(0);
0xa0601234;
Width16Bit;
ZeroExt;
}
load_prefixed! {
"plhz" 3, 0x123456789(4), 0;
0x06012345, 0xa0646789;
Width16Bit;
ZeroExt;
}
load_prefixed! {
"plhz" 3, 0x123456789(0), 0;
0x06012345, 0xa0606789;
Width16Bit;
ZeroExt;
}
load_prefixed! {
"plhz" 3, 0x123456789(0), 1;
0x06112345, 0xa0606789;
Width16Bit;
ZeroExt;
}
load_indexed! {
"lhzx" 3, 4, 5;
0x7c642a2e;
Width16Bit;
ZeroExt;
}
load_indexed! {
"lhzx" 3, 0, 5;
0x7c602a2e;
Width16Bit;
ZeroExt;
}
load_update! {
"lhzu" 3, 0x1234(4);
0xa4641234;
Width16Bit;
ZeroExt;
}
load_update_indexed! {
"lhzux" 3, 4, 5;
0x7c642a6e;
Width16Bit;
ZeroExt;
}
load! {
"lha" 3, 0x1234(4);
0xa8641234;
Width16Bit;
SignExt;
}
load! {
"lha" 3, 0x1234(0);
0xa8601234;
Width16Bit;
SignExt;
}
load_prefixed! {
"plha" 3, 0x123456789(4), 0;
0x06012345, 0xa8646789;
Width16Bit;
SignExt;
}
load_prefixed! {
"plha" 3, 0x123456789(0), 0;
0x06012345, 0xa8606789;
Width16Bit;
SignExt;
}
load_prefixed! {
"plha" 3, 0x123456789(0), 1;
0x06112345, 0xa8606789;
Width16Bit;
SignExt;
}
load_indexed! {
"lhax" 3, 4, 5;
0x7c642aae;
Width16Bit;
SignExt;
}
load_indexed! {
"lhax" 3, 0, 5;
0x7c602aae;
Width16Bit;
SignExt;
}
load_update! {
"lhau" 3, 0x1234(4);
0xac641234;
Width16Bit;
SignExt;
}
load_update_indexed! {
"lhaux" 3, 4, 5;
0x7c642aee;
Width16Bit;
SignExt;
}
load! {
"lwz" 3, 0x1234(4);
0x80641234;
Width32Bit;
ZeroExt;
}
load! {
"lwz" 3, 0x1234(0);
0x80601234;
Width32Bit;
ZeroExt;
}
load_prefixed! {
"plwz" 3, 0x123456789(4), 0;
0x06012345, 0x80646789;
Width32Bit;
ZeroExt;
}
load_prefixed! {
"plwz" 3, 0x123456789(0), 0;
0x06012345, 0x80606789;
Width32Bit;
ZeroExt;
}
load_prefixed! {
"plwz" 3, 0x123456789(0), 1;
0x06112345, 0x80606789;
Width32Bit;
ZeroExt;
}
load_indexed! {
"lwzx" 3, 4, 5;
0x7c64282e;
Width32Bit;
ZeroExt;
}
load_indexed! {
"lwzx" 3, 0, 5;
0x7c60282e;
Width32Bit;
ZeroExt;
}
load_update! {
"lwzu" 3, 0x1234(4);
0x84641234;
Width32Bit;
ZeroExt;
}
load_update_indexed! {
"lwzux" 3, 4, 5;
0x7c64286e;
Width32Bit;
ZeroExt;
}
load! {
"lwa" 3, 0x1234(4);
0xe8641236;
Width32Bit;
SignExt;
}
load! {
"lwa" 3, 0x1234(0);
0xe8601236;
Width32Bit;
SignExt;
}
load_prefixed! {
"plwa" 3, 0x123456789(4), 0;
0x04012345, 0xa4646789;
Width32Bit;
SignExt;
}
load_prefixed! {
"plwa" 3, 0x123456789(0), 0;
0x04012345, 0xa4606789;
Width32Bit;
SignExt;
}
load_prefixed! {
"plwa" 3, 0x123456789(0), 1;
0x04112345, 0xa4606789;
Width32Bit;
SignExt;
}
load_indexed! {
"lwax" 3, 4, 5;
0x7c642aaa;
Width32Bit;
SignExt;
}
load_indexed! {
"lwax" 3, 0, 5;
0x7c602aaa;
Width32Bit;
SignExt;
}
// there is no `lwau`
load_update_indexed! {
"lwaux" 3, 4, 5;
0x7c642aea;
Width32Bit;
SignExt;
}
load! {
"ld" 3, 0x1234(4);
0xe8641234;
Width64Bit;
ZeroExt;
}
load! {
"ld" 3, 0x1234(0);
0xe8601234;
Width64Bit;
ZeroExt;
}
load_prefixed! {
"pld" 3, 0x123456789(4), 0;
0x04012345, 0xe4646789;
Width64Bit;
ZeroExt;
}
load_prefixed! {
"pld" 3, 0x123456789(0), 0;
0x04012345, 0xe4606789;
Width64Bit;
ZeroExt;
}
load_prefixed! {
"pld" 3, 0x123456789(0), 1;
0x04112345, 0xe4606789;
Width64Bit;
ZeroExt;
}
load_indexed! {
"ldx" 3, 4, 5;
0x7c64282a;
Width64Bit;
ZeroExt;
}
load_indexed! {
"ldx" 3, 0, 5;
0x7c60282a;
Width64Bit;
ZeroExt;
}
load_update! {
"ldu" 3, 0x1234(4);
0xe8641235;
Width64Bit;
ZeroExt;
}
load_update_indexed! {
"ldux" 3, 4, 5;
0x7c64286a;
Width64Bit;
ZeroExt;
}
}

View file

@ -1,273 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_empty, insn_single};
use cpu::instruction::{LogicalMOp, Lut4, MOpDestReg, MOpRegNum, MoveRegMOp, OutputIntegerMode};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.13 Fixed-Point Logical Instructions
pub fn test_cases_book_i_3_3_13_fixed_point_logical(retval: &mut Vec<TestCase>) {
macro_rules! insn_logic_i {
(
$mnemonic:literal $dest:literal, $src:literal, $imm:literal;
$encoding:literal;
|$a:ident, $b:ident| $lut_fn:expr;
) => {
retval.push(insn_single(
concat!(
$mnemonic,
" ",
stringify!($dest),
", ",
stringify!($src),
", ",
stringify!($imm)
),
$encoding,
None,
LogicalMOp::logical_i(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[MOpRegNum::power_isa_gpr_reg_imm($src).value],
(($imm as u32) << if $mnemonic.contains('s') { 16 } else { 0 })
.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
Lut4::from_fn(|$a, $b| $lut_fn),
),
));
};
}
insn_logic_i! {
"andi." 3, 4, 0x89ab;
0x708389ab;
|a, b| a & b;
}
insn_logic_i! {
"andis." 3, 4, 0x89ab;
0x748389ab;
|a, b| a & b;
}
insn_logic_i! {
"ori" 3, 4, 0x89ab;
0x608389ab;
|a, b| a | b;
}
// ensure nop decodes to zero instructions
retval.push(insn_empty("ori 0, 0, 0", 0x60000000, None));
insn_logic_i! {
"oris" 3, 4, 0x89ab;
0x648389ab;
|a, b| a | b;
}
insn_logic_i! {
"xori" 3, 4, 0x89ab;
0x688389ab;
|a, b| a ^ b;
}
insn_logic_i! {
"xori" 0, 0, 0; // ensure xnop actually decodes to a normal ALU instruction
0x68000000;
|a, b| a ^ b;
}
insn_logic_i! {
"xoris" 3, 4, 0x89ab;
0x6c8389ab;
|a, b| a ^ b;
}
macro_rules! insn_logic {
(
$mnemonic:literal $dest:literal, $src0:literal, $src1:literal;
$encoding:literal;
|$a:ident, $b:ident| $lut_fn:expr;
) => {
retval.push(insn_single(
concat!(
$mnemonic,
" ",
stringify!($dest),
", ",
stringify!($src0),
", ",
stringify!($src1)
),
$encoding,
None,
LogicalMOp::logical(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[
MOpRegNum::power_isa_gpr_reg_imm($src0).value,
MOpRegNum::power_isa_gpr_reg_imm($src1).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
Lut4::from_fn(|$a, $b| $lut_fn),
),
));
};
}
insn_logic! {
"and" 3, 4, 5;
0x7c832838;
|a, b| a & b;
}
insn_logic! {
"and." 3, 4, 5;
0x7c832839;
|a, b| a & b;
}
insn_logic! {
"xor" 3, 4, 5;
0x7c832a78;
|a, b| a ^ b;
}
insn_logic! {
"xor." 3, 4, 5;
0x7c832a79;
|a, b| a ^ b;
}
insn_logic! {
"nand" 3, 4, 5;
0x7c832bb8;
|a, b| !(a & b);
}
insn_logic! {
"nand." 3, 4, 5;
0x7c832bb9;
|a, b| !(a & b);
}
insn_logic! {
"or" 3, 4, 5;
0x7c832b78;
|a, b| a | b;
}
retval.push(insn_single(
"or 3, 4, 4", // mr 3, 4
0x7c832378,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(4).value],
0.cast_to_static::<SInt<_>>(),
),
));
insn_logic! {
"or." 3, 4, 5;
0x7c832b79;
|a, b| a | b;
}
insn_logic! {
"or." 3, 4, 4; // mr. 3, 4
0x7c832379;
|a, b| a | b;
}
insn_logic! {
"orc" 3, 4, 5;
0x7c832b38;
|a, b| a | !b;
}
insn_logic! {
"orc." 3, 4, 5;
0x7c832b39;
|a, b| a | !b;
}
insn_logic! {
"nor" 3, 4, 5;
0x7c8328f8;
|a, b| !(a | b);
}
insn_logic! {
"nor." 3, 4, 5;
0x7c8328f9;
|a, b| !(a | b);
}
insn_logic! {
"eqv" 3, 4, 5;
0x7c832a38;
|a, b| a == b;
}
insn_logic! {
"eqv." 3, 4, 5;
0x7c832a39;
|a, b| a == b;
}
insn_logic! {
"andc" 3, 4, 5;
0x7c832878;
|a, b| a & !b;
}
insn_logic! {
"andc." 3, 4, 5;
0x7c832879;
|a, b| a & !b;
}
macro_rules! insn_exts {
(
$mnemonic:literal $dest:literal, $src:literal;
$encoding:literal;
$OutputIntegerMode:ident;
) => {
retval.push(insn_single(
concat!($mnemonic, " ", stringify!($dest), ", ", stringify!($src)),
$encoding,
None,
LogicalMOp::logical_i(
MOpDestReg::new_sim(
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
if $mnemonic.contains('.') {
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
} else {
&[]
},
),
[MOpRegNum::power_isa_gpr_reg_imm($src).value],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.$OutputIntegerMode(),
Lut4::from_fn(|a, b| a | b),
),
));
};
}
insn_exts! {
"extsb" 3, 4;
0x7c830774;
SignExt8;
}
insn_exts! {
"extsb." 3, 4;
0x7c830775;
SignExt8;
}
insn_exts! {
"extsh" 3, 4;
0x7c830734;
SignExt16;
}
insn_exts! {
"extsh." 3, 4;
0x7c830735;
SignExt16;
}
insn_exts! {
"extsw" 3, 4;
0x7c8307b4;
SignExt32;
}
insn_exts! {
"extsw." 3, 4;
0x7c8307b5;
SignExt32;
}
}

View file

@ -1,512 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_double, insn_triple};
use cpu::instruction::{
AddSubMOp, LoadStoreConversion, LoadStoreWidth, MOpDestReg, MOpRegNum, MoveRegMOp,
OutputIntegerMode, StoreMOp,
};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.3 Fixed-Point Store Instructions
pub fn test_cases_book_i_3_3_3_fixed_point_store(retval: &mut Vec<TestCase>) {
macro_rules! store_prefixed {
(
$mnemonic:literal $rs:literal, $disp:literal($ra:literal), $r:literal;
$prefix:literal, $suffix:literal;
$width:ident;
) => {
retval.push(insn_double(
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, "), ", $r),
$prefix,
Some($suffix),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $r != 0 || $ra == 0 {
MOpRegNum::const_zero().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
$r != 0,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
};
}
macro_rules! store {
(
$mnemonic:literal $rs:literal, $disp:literal($ra:literal);
$encoding:literal;
$width:ident;
) => {
retval.push(insn_double(
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
};
}
macro_rules! store_update {
(
$mnemonic:literal $rs:literal, $disp:literal($ra:literal);
$encoding:literal;
$width:ident;
) => {
if $ra == $rs {
retval.push(insn_triple(
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[MOpRegNum::power_isa_temp_reg().value],
0.cast_to_static::<SInt<_>>(),
),
],
));
} else {
retval.push(insn_double(
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
MOpRegNum::const_zero().value,
],
($disp as i64).cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
};
}
macro_rules! store_indexed {
(
$mnemonic:literal $rs:literal, $ra:literal, $rb:literal;
$encoding:literal;
$width:ident;
) => {
retval.push(insn_double(
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
};
}
macro_rules! store_update_indexed {
(
$mnemonic:literal $rs:literal, $ra:literal, $rb:literal;
$encoding:literal;
$width:ident;
) => {
if $ra == $rs {
retval.push(insn_triple(
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[MOpRegNum::power_isa_temp_reg().value],
0.cast_to_static::<SInt<_>>(),
),
],
));
} else {
retval.push(insn_double(
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
$encoding,
None,
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
[
if $ra == 0 {
MOpRegNum::const_zero().value
} else {
MOpRegNum::power_isa_gpr_reg_imm($ra).value
},
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
],
LoadStoreWidth.$width(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
};
}
store! {
"stb" 3, 0x1234(4);
0x98641234;
Width8Bit;
}
store! {
"stb" 3, 0x1234(0);
0x98601234;
Width8Bit;
}
store_prefixed! {
"pstb" 3, 0x123456789(4), 0;
0x06012345, 0x98646789;
Width8Bit;
}
store_prefixed! {
"pstb" 3, 0x123456789(0), 0;
0x06012345, 0x98606789;
Width8Bit;
}
store_prefixed! {
"pstb" 3, 0x123456789(0), 1;
0x06112345, 0x98606789;
Width8Bit;
}
store_indexed! {
"stbx" 3, 4, 5;
0x7c6429ae;
Width8Bit;
}
store_indexed! {
"stbx" 3, 0, 5;
0x7c6029ae;
Width8Bit;
}
store_update! {
"stbu" 3, 0x1234(4);
0x9c641234;
Width8Bit;
}
store_update! {
"stbu" 3, 0x1234(3);
0x9c631234;
Width8Bit;
}
store_update_indexed! {
"stbux" 3, 4, 5;
0x7c6429ee;
Width8Bit;
}
store_update_indexed! {
"stbux" 3, 3, 5;
0x7c6329ee;
Width8Bit;
}
store! {
"sth" 3, 0x1234(4);
0xb0641234;
Width16Bit;
}
store! {
"sth" 3, 0x1234(0);
0xb0601234;
Width16Bit;
}
store_prefixed! {
"psth" 3, 0x123456789(4), 0;
0x06012345, 0xb0646789;
Width16Bit;
}
store_prefixed! {
"psth" 3, 0x123456789(0), 0;
0x06012345, 0xb0606789;
Width16Bit;
}
store_prefixed! {
"psth" 3, 0x123456789(0), 1;
0x06112345, 0xb0606789;
Width16Bit;
}
store_indexed! {
"sthx" 3, 4, 5;
0x7c642b2e;
Width16Bit;
}
store_indexed! {
"sthx" 3, 0, 5;
0x7c602b2e;
Width16Bit;
}
store_update! {
"sthu" 3, 0x1234(4);
0xb4641234;
Width16Bit;
}
store_update! {
"sthu" 3, 0x1234(3);
0xb4631234;
Width16Bit;
}
store_update_indexed! {
"sthux" 3, 4, 5;
0x7c642b6e;
Width16Bit;
}
store_update_indexed! {
"sthux" 3, 3, 5;
0x7c632b6e;
Width16Bit;
}
store! {
"stw" 3, 0x1234(4);
0x90641234;
Width32Bit;
}
store! {
"stw" 3, 0x1234(0);
0x90601234;
Width32Bit;
}
store_prefixed! {
"pstw" 3, 0x123456789(4), 0;
0x06012345, 0x90646789;
Width32Bit;
}
store_prefixed! {
"pstw" 3, 0x123456789(0), 0;
0x06012345, 0x90606789;
Width32Bit;
}
store_prefixed! {
"pstw" 3, 0x123456789(0), 1;
0x06112345, 0x90606789;
Width32Bit;
}
store_indexed! {
"stwx" 3, 4, 5;
0x7c64292e;
Width32Bit;
}
store_indexed! {
"stwx" 3, 0, 5;
0x7c60292e;
Width32Bit;
}
store_update! {
"stwu" 3, 0x1234(4);
0x94641234;
Width32Bit;
}
store_update! {
"stwu" 3, 0x1234(3);
0x94631234;
Width32Bit;
}
store_update_indexed! {
"stwux" 3, 4, 5;
0x7c64296e;
Width32Bit;
}
store_update_indexed! {
"stwux" 3, 3, 5;
0x7c63296e;
Width32Bit;
}
store! {
"std" 3, 0x1234(4);
0xf8641234;
Width64Bit;
}
store! {
"std" 3, 0x1234(0);
0xf8601234;
Width64Bit;
}
store_prefixed! {
"pstd" 3, 0x123456789(4), 0;
0x04012345, 0xf4646789;
Width64Bit;
}
store_prefixed! {
"pstd" 3, 0x123456789(0), 0;
0x04012345, 0xf4606789;
Width64Bit;
}
store_prefixed! {
"pstd" 3, 0x123456789(0), 1;
0x04112345, 0xf4606789;
Width64Bit;
}
store_indexed! {
"stdx" 3, 4, 5;
0x7c64292a;
Width64Bit;
}
store_indexed! {
"stdx" 3, 0, 5;
0x7c60292a;
Width64Bit;
}
store_update! {
"stdu" 3, 0x1234(4);
0xf8641235;
Width64Bit;
}
store_update! {
"stdu" 3, 0x1234(3);
0xf8631235;
Width64Bit;
}
store_update_indexed! {
"stdux" 3, 4, 5;
0x7c64296a;
Width64Bit;
}
store_update_indexed! {
"stdux" 3, 3, 5;
0x7c63296a;
Width64Bit;
}
}

View file

@ -1,149 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_single};
use cpu::instruction::{
LogicalFlagsMOp, LogicalFlagsMOpImm, Lut4, MOpDestReg, MOpRegNum, MoveRegMOp, ReadSpecialMOp,
ReadSpecialMOpImm,
};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.19 Move To/From System Register Instructions
pub fn test_cases_book_i_3_3_19_move_to_from_system_register(retval: &mut Vec<TestCase>) {
// mfspr/mtspr are covered by test_cases_book_iii_5_4_4_move_to_from_system_register
#[hdl]
fn mcrxrx_imm() -> SimValue<LogicalFlagsMOpImm> {
#[hdl(sim)]
LogicalFlagsMOpImm {
// if the order of flags in PRegFlags changes, this will need to be updated
src0_start: 4usize.cast_to(LogicalFlagsMOpImm.src0_start),
src1_start: 4usize.cast_to(LogicalFlagsMOpImm.src1_start),
src2_start: 4usize.cast_to(LogicalFlagsMOpImm.src2_start),
dest_start: 0usize.cast_to(LogicalFlagsMOpImm.dest_start),
dest_count: 6usize.cast_to(LogicalFlagsMOpImm.dest_count),
}
}
retval.push(insn_single(
"mcrxrx 3",
0x7d800480,
None,
LogicalFlagsMOp::logical_flags(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
MOpRegNum::const_zero().value,
MOpRegNum::power_isa_xer_so_ov_ov32_reg().value,
],
mcrxrx_imm(),
Lut4::from_fn(|a, b| a | b),
),
));
}
/// covers instructions in PowerISA v3.1C Book III 5.4.4 Move To/From System Register Instructions
pub fn test_cases_book_iii_5_4_4_move_to_from_system_register(retval: &mut Vec<TestCase>) {
retval.push(insn_single(
"mflr 3",
0x7c6802a6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_lr_reg().value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mtlr 3",
0x7c6803a6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mfctr 3",
0x7c6902a6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_ctr_reg().value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mtctr 3",
0x7c6903a6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_CTR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mfspr 3, 815 # mftar 3",
0x7c6fcaa6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_tar_reg().value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mtspr 815, 3 # mttar 3",
0x7c6fcba6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TAR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
0.cast_to_static::<SInt<_>>(),
),
));
// make sure we generate mfspr and not the phased-out mftb
retval.push(insn_single(
"mfspr 3, 268 # mftb 3",
0x7c6c42a6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; 0],
ReadSpecialMOpImm.PowerIsaTimeBase(),
),
));
// make sure we generate mfspr and not the phased-out mftb
retval.push(insn_single(
"mfspr 3, 269 # mftbu 3",
0x7c6d42a6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; 0],
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
),
));
// phased-out mftb -- not actually generated by the assembler so we have to use .long
retval.push(insn_single(
".long 0x7c6c42e6 # mftb 3, 268",
0x7c6c42e6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; 0],
ReadSpecialMOpImm.PowerIsaTimeBase(),
),
));
// phased-out mftb -- not actually generated by the assembler so we have to use .long
retval.push(insn_single(
".long 0x7c6d42e6 # mftb 3, 269",
0x7c6d42e6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; 0],
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
),
));
}

View file

@ -1,16 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_empty};
/// covers instructions in PowerISA v3.1C Book I 3.3.20 Prefixed No-Operation Instruction
pub fn test_cases_book_i_3_3_20_prefixed_no_operation(retval: &mut Vec<TestCase>) {
// ensure pnop decodes to zero instructions
retval.push(insn_empty(
// LLVM doesn't support the pnop instruction:
// https://github.com/llvm/llvm-project/issues/176831
".long 0x07000000, 0 # pnop",
0x07000000,
Some(0),
));
}

View file

@ -31,7 +31,6 @@ function check_file()
POUND_HEADER=('^"# SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"# See Notices.txt for copyright information"$') POUND_HEADER=('^"# SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"# See Notices.txt for copyright information"$')
SLASH_HEADER=('^"// SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"// See Notices.txt for copyright information"$') SLASH_HEADER=('^"// SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"// See Notices.txt for copyright information"$')
MD_HEADER=('^"<!--"$' '^"SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"See Notices.txt for copyright information"$') MD_HEADER=('^"<!--"$' '^"SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"See Notices.txt for copyright information"$')
MERMAID_HEADER=('^"%% SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"%% See Notices.txt for copyright information"$')
function main() function main()
{ {
@ -46,15 +45,12 @@ function main()
*/LICENSE.md|*/Notices.txt) */LICENSE.md|*/Notices.txt)
# copyright file # copyright file
;; ;;
/crates/cpu/tests*/expected/*.vcd|/crates/cpu/tests*/expected/*.txt) /crates/cpu/tests/expected/*.vcd|/crates/cpu/tests/expected/*.txt)
# file that can't contain copyright header # file that can't contain copyright header
;; ;;
/.forgejo/workflows/*.yml|*/.gitignore|*/.gitattributes|*.toml) /.forgejo/workflows/*.yml|*/.gitignore|*.toml)
check_file "$file" "${POUND_HEADER[@]}" check_file "$file" "${POUND_HEADER[@]}"
;; ;;
*.mermaid)
check_file "$file" "${MERMAID_HEADER[@]}"
;;
*.md) *.md)
check_file "$file" "${MD_HEADER[@]}" check_file "$file" "${MD_HEADER[@]}"
;; ;;