Compare commits

..

4 commits

Author SHA1 Message Date
921cddbe06
disable tests/reg_alloc.rs for now
All checks were successful
/ deps (push) Successful in 17s
/ test (push) Successful in 51m28s
2025-03-18 17:26:47 -07:00
b9d68c0019
WIP: splitting reg_alloc
Some checks failed
/ deps (push) Successful in 14s
/ test (push) Failing after 2m7s
2025-03-14 18:26:34 -07:00
9fa959652e
update fayalite 2025-03-10 19:50:55 -07:00
9199fdf35c
add name_mangling_serde crate 2025-03-08 22:38:30 -08:00
53 changed files with 38484 additions and 526684 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,16 +3,55 @@
on: [push, pull_request]
jobs:
deps:
uses: ./.forgejo/workflows/deps.yml
test:
runs-on: debian-12
container:
image: git.libre-chip.org/libre-chip/fayalite-deps:latest
needs: deps
steps:
- uses: actions/checkout@v3
- uses: https://git.libre-chip.org/mirrors/checkout@v3
with:
fetch-depth: 0
- run: |
scripts/check-copyright.sh
- run: |
apt-get update -qq
apt-get install -qq \
bison \
build-essential \
ccache \
clang \
cvc5 \
flex \
gawk \
git \
libboost-filesystem-dev \
libboost-python-dev \
libboost-system-dev \
libffi-dev \
libreadline-dev \
lld \
pkg-config \
python3 \
python3-click \
tcl-dev \
z3 \
zlib1g-dev
- run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.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
with:
save-if: ${{ github.ref == 'refs/heads/master' }}

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
# See Notices.txt for copyright information
/target
OPF_PowerISA_v3.1C.pdf

605
Cargo.lock generated
View file

@ -1,20 +1,17 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
version = 3
[[package]]
name = "adler2"
version = "2.0.1"
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"memchr",
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
@ -96,36 +93,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "bitflags"
version = "2.6.0"
@ -168,48 +135,21 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bytes"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "cc"
version = "1.2.51"
version = "1.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "clap"
version = "4.5.20"
@ -232,15 +172,6 @@ dependencies = [
"strsim",
]
[[package]]
name = "clap_complete"
version = "4.5.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2348487adcd4631696ced64ccdb40d38ac4d31cae7f2eec8817fcea1b9d1c43c"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "4.5.18"
@ -275,17 +206,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
name = "cpu"
version = "0.1.0"
dependencies = [
"base16ct 1.0.0",
"fayalite",
"hex-literal",
"parse_powerisa_pdf",
"regex",
"roxmltree",
"name_mangling_serde",
"serde",
"sha2",
"simple-mermaid",
"ureq",
"which",
]
[[package]]
@ -297,15 +220,6 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -361,12 +275,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.14"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@ -388,22 +302,20 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fayalite"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8"
dependencies = [
"base64",
"bitvec",
"blake3",
"clap",
"clap_complete",
"ctor",
"eyre",
"fayalite-proc-macros",
"fayalite-visit-gen",
"hashbrown 0.15.5",
"hashbrown",
"jobslot",
"num-bigint",
"num-traits",
"ordered-float",
"os_pipe",
"petgraph",
"serde",
"serde_json",
@ -415,7 +327,7 @@ dependencies = [
[[package]]
name = "fayalite-proc-macros"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8"
dependencies = [
"fayalite-proc-macros-impl",
]
@ -423,9 +335,9 @@ dependencies = [
[[package]]
name = "fayalite-proc-macros-impl"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8"
dependencies = [
"base16ct 0.2.0",
"base16ct",
"num-bigint",
"prettyplease",
"proc-macro2",
@ -438,7 +350,7 @@ dependencies = [
[[package]]
name = "fayalite-visit-gen"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8"
dependencies = [
"indexmap",
"prettyplease",
@ -450,34 +362,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "funty"
version = "2.0.0"
@ -496,62 +386,31 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.16"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"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]]
name = "hashbrown"
version = "0.15.5"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hex-literal"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1"
[[package]]
name = "home"
version = "0.5.9"
@ -561,22 +420,6 @@ dependencies = [
"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]]
name = "indenter"
version = "0.3.3"
@ -585,14 +428,13 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "2.13.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"hashbrown",
"serde",
"serde_core",
]
[[package]]
@ -601,15 +443,6 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -618,39 +451,23 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jobslot"
version = "0.2.23"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c"
checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d"
dependencies = [
"cfg-if",
"derive_destructure2",
"getrandom 0.3.4",
"getrandom",
"libc",
"scopeguard",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
name = "libc"
version = "0.2.180"
version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[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"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "linux-raw-sys"
@ -658,12 +475,6 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "memchr"
version = "2.7.4"
@ -671,42 +482,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
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"
name = "name_mangling_serde"
version = "0.1.0"
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",
"serde",
"serde_json",
]
[[package]]
@ -744,51 +524,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "ordered-float"
version = "5.1.0"
name = "os_pipe"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [
"num-traits",
"rand",
"serde",
"libc",
"windows-sys 0.59.0",
]
[[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]]
name = "petgraph"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
version = "0.6.5"
source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f"
dependencies = [
"fixedbitset",
"hashbrown 0.15.5",
"indexmap",
"serde",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "prettyplease"
version = "0.2.22"
@ -808,15 +561,6 @@ dependencies = [
"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]]
name = "quote"
version = "1.0.37"
@ -826,95 +570,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
"serde",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"serde",
]
[[package]]
name = "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]]
name = "rustix"
version = "0.38.37"
@ -928,41 +589,6 @@ dependencies = [
"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]]
name = "ryu"
version = "1.0.18"
@ -977,28 +603,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.228"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
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"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
@ -1020,9 +636,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.9"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@ -1035,30 +651,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.96"
@ -1121,47 +719,6 @@ version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "utf8parse"
version = "0.2.2"
@ -1182,27 +739,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
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"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[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",
]
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "which"
@ -1212,17 +751,10 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
dependencies = [
"either",
"home",
"regex",
"rustix",
"winsafe",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.52.0"
@ -1241,15 +773,6 @@ dependencies = [
"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]]
name = "windows-targets"
version = "0.52.6"
@ -1320,12 +843,6 @@ version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "wyz"
version = "0.5.1"
@ -1337,26 +854,20 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.27"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.27"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"

View file

@ -7,23 +7,17 @@ members = ["crates/*"]
[workspace.package]
version = "0.1.0"
license = "LGPL-3.0-or-later"
edition = "2024"
edition = "2021"
repository = ""
keywords = []
categories = []
rust-version = "1.89.0"
rust-version = "1.82.0"
[workspace.dependencies]
base16ct = "1.0.0"
name_mangling_serde = { version = "=0.1.0", path = "crates/name_mangling_serde" }
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"] }
serde_json = { version = "1.0.117", features = ["preserve_order"] }
[profile.dev]
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,8 @@ version.workspace = true
[dependencies]
fayalite.workspace = true
roxmltree.workspace = true
serde.workspace = true
simple-mermaid.workspace = true
name_mangling_serde.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
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(todo)'] }

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

@ -1,17 +1,17 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
instruction::{CONST_ZERO_UNIT_NUM, MOpTrait, PRegNum, RenamedMOp, UnitNum, UnitOutRegNum},
unit::{
UnitCancelInput, UnitKind, UnitOutputWrite,
unit_base::{UnitForwardingInfo, UnitToRegAlloc},
},
instruction::{PRegNum, CONST_ZERO_UNIT_NUM},
unit::UnitKind,
};
use fayalite::{
intern::{Intern, Interned},
prelude::*,
};
use fayalite::prelude::*;
use serde::{Deserialize, Serialize};
use std::num::NonZeroUsize;
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UnitConfig {
pub kind: UnitKind,
@ -28,18 +28,14 @@ impl UnitConfig {
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CpuConfig {
pub units: Vec<UnitConfig>,
pub units: Interned<[UnitConfig]>,
pub out_reg_num_width: usize,
pub fetch_width: NonZeroUsize,
pub max_branches_per_fetch: NonZeroUsize,
pub max_fetches_in_flight: NonZeroUsize,
pub log2_fetch_width_in_bytes: u8,
/// default value for [`UnitConfig::max_in_flight`]
pub default_unit_max_in_flight: NonZeroUsize,
pub rob_size: NonZeroUsize,
}
impl CpuConfig {
@ -50,35 +46,18 @@ impl CpuConfig {
};
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_UNIT_MAX_IN_FLIGHT: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(8) else {
unreachable!();
};
v
};
pub fn new(units: Vec<UnitConfig>, rob_size: NonZeroUsize) -> Self {
pub fn new(units: Interned<[UnitConfig]>) -> Self {
Self {
units,
out_reg_num_width: Self::DEFAULT_OUT_REG_NUM_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,
default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT,
rob_size,
}
}
pub fn non_const_unit_nums(&self) -> std::ops::Range<usize> {
@ -87,91 +66,83 @@ impl CpuConfig {
pub fn unit_num_width(&self) -> usize {
UInt::range(CONST_ZERO_UNIT_NUM..self.non_const_unit_nums().end).width()
}
pub fn unit_num(&self) -> UnitNum<DynSize> {
UnitNum[self.unit_num_width()]
}
pub fn unit_out_reg_num(&self) -> UnitOutRegNum<DynSize> {
UnitOutRegNum[self.out_reg_num_width]
}
pub fn p_reg_num(&self) -> PRegNum<DynSize, DynSize> {
PRegNum[self.unit_num_width()][self.out_reg_num_width]
}
pub fn p_reg_num_width(&self) -> usize {
self.unit_num_width() + self.out_reg_num_width
}
pub fn renamed_mop_in_unit(&self) -> RenamedMOp<UnitOutRegNum<DynSize>, DynSize> {
RenamedMOp[self.unit_out_reg_num()][self.p_reg_num_width()]
}
pub fn unit_output_write(&self) -> UnitOutputWrite<DynSize> {
UnitOutputWrite[self.out_reg_num_width]
}
pub fn unit_output_writes(&self) -> Array<HdlOption<UnitOutputWrite<DynSize>>> {
Array[HdlOption[self.unit_output_write()]][self.non_const_unit_nums().len()]
}
pub fn unit_cancel_input(&self) -> UnitCancelInput<DynSize> {
UnitCancelInput[self.out_reg_num_width]
}
pub fn unit_forwarding_info(&self) -> UnitForwardingInfo<DynSize, DynSize, DynSize> {
UnitForwardingInfo[self.unit_num_width()][self.out_reg_num_width]
[self.non_const_unit_nums().len()]
}
pub fn unit_max_in_flight(&self, unit_index: usize) -> NonZeroUsize {
self.units[unit_index]
.max_in_flight
.unwrap_or(self.default_unit_max_in_flight)
}
pub fn unit_to_reg_alloc<
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>,
ExtraOut: Type,
>(
&self,
mop_ty: MOp,
extra_out_ty: ExtraOut,
) -> UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> {
assert_eq!(
mop_ty.dest_reg_ty(),
self.unit_out_reg_num(),
"inconsistent types",
);
UnitToRegAlloc[mop_ty][extra_out_ty][self.unit_num_width()][self.out_reg_num_width]
[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 retire_queue_index_width(&self) -> usize {
let max_in_flight: usize = (0..self.units.len())
.map(|unit_index| self.unit_max_in_flight(unit_index).get())
.sum();
2 + max_in_flight.next_power_of_two().ilog2() as usize
}
}
#[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.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>
{
mod sealed {
pub trait Sealed {}
}
impl PhantomConstCpuConfig for PhantomConst<CpuConfig> {}
impl sealed::Sealed for PhantomConst<CpuConfig> {}
pub trait CpuConfigType: Type + ToExpr<Type = Self> + sealed::Sealed {
fn get(self) -> Interned<CpuConfig>;
}
impl CpuConfigType for PhantomConst<CpuConfig> {
fn get(self) -> Interned<CpuConfig> {
self.get()
}
}
pub trait Identity<Arg: ?Sized> {
type SelfType: ?Sized;
type ArgType: ?Sized;
}
impl<T: ?Sized, Arg: ?Sized> Identity<Arg> for T {
type SelfType = T;
type ArgType = Arg;
}
macro_rules! impl_cpu_config_accessors {
(
$(
#[without_generics = $without_generics:ident]
$vis:vis type $ident:ident<$T:ident> = |$arg:ident| $expr:expr;
)*
) => {
$(
#[allow(non_camel_case_types)]
$vis struct $without_generics;
#[allow(non_upper_case_globals)]
$vis const $ident: $without_generics = $without_generics;
$vis type $ident<$T> = <DynSize as Identity<$T>>::SelfType;
impl<$T: CpuConfigType> std::ops::Index<$T> for $without_generics {
type Output = usize;
fn index(&self, $arg: $T) -> &Self::Output {
Interned::into_inner(Intern::intern_sized($expr))
}
}
)*
};
}
impl_cpu_config_accessors! {
#[without_generics = __UnitNumWidth_WithoutGenerics]
pub type UnitNumWidth<T> = |arg| arg.get().unit_num_width();
#[without_generics = __UnitOutRegNumWidth_WithoutGenerics]
pub type UnitOutRegNumWidth<T> = |arg| arg.get().out_reg_num_width;
#[without_generics = __PRegNumWidth_WithoutGenerics]
pub type PRegNumWidth<T> = |arg| PRegNum[arg].canonical().bit_width();
#[without_generics = __RetireQueueIndexWidth_WithoutGenerics]
pub type RetireQueueIndexWidth<T> = |arg| arg.get().retire_queue_index_width();
#[without_generics = __UnitCount_WithoutGenerics]
pub type UnitCount<T> = |arg| arg.get().non_const_unit_nums().len();
#[without_generics = __FetchWidth_WithoutGenerics]
pub type FetchWidth<T> = |arg| arg.get().fetch_width.get();
}

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

View file

@ -27,432 +27,26 @@ pub struct PowerIsaCrBitNum {
impl MOpRegNum {
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;
#[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;
#[hdl]
pub fn power_isa_tar_reg() -> Expr<Self> {
#[hdl]
Self {
value: Self::POWER_ISA_TAR_REG_NUM.cast_to_static::<UInt<_>>(),
}
}
/// XER bits are stored in [`PRegValue.flags`], bits that don't exist in [`PRegValue.flags`] are stored in [`PRegValue.int_fp`]
///
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
/// [`PRegValue.int_fp`]: struct@crate::register::PRegValue
pub const POWER_ISA_XER_REG_NUM: u32 = 4;
/// SO, OV, and OV32 XER bits -- in [`PRegValue.flags`]
///
/// [`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 POWER_ISA_CR_REG_NUMS: Range<u32> = 8..16;
pub const fn power_isa_cr_reg_num(index: usize) -> u32 {
if index == 0 {
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<_>>(),
}
range_u32_nth_or_panic(&Self::POWER_ISA_CR_REG_NUMS, index)
}
pub const POWER_ISA_GPR_REG_NUMS: Range<u32> = 32..64;
pub const fn power_isa_gpr_reg_num(index: usize) -> u32 {
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 fn power_isa_fpr_reg_num(index: usize) -> u32 {
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

@ -0,0 +1,261 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{CpuConfig, CpuConfigType, FetchWidth, PRegNumWidth},
instruction::{MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum, RenamedMOp},
rename_table::{rename_table, RenameTablePortConfig},
unit::{RenamedInsnData, RetireQueueIndex, UnitMOp},
util::array_vec::{ArrayVec, Length, ReadyValidArray},
};
use fayalite::{
prelude::*,
util::{prefix_sum::PrefixSumAlgorithm, ready_valid::ReadyValid},
};
#[hdl(no_static)]
pub struct InstructionRenameInputInsn<C: Type + CpuConfigType> {
pub mop: MOp,
pub pc: UInt<64>,
pub renamed_dest: PRegNum<C>,
}
#[hdl(no_static)]
struct InsnsInPrefixSummary<C: Type + CpuConfigType> {
all_ready: Bool,
ready_count: Length<FetchWidth<C>>,
retire_queue_used: Length<FetchWidth<C>>,
config: C,
}
type C = PhantomConst<CpuConfig>;
#[hdl]
pub type InstructionRenameInsnsOut<C: Type + CpuConfigType> = ArrayType<
ReadyValid<RenamedInsnData<C, RenamedMOp<PRegNumWidth<C>>, PRegNum<C>>>,
FetchWidth<C>,
>;
#[hdl_module]
pub fn instruction_rename(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let insns_in: ReadyValidArray<InstructionRenameInputInsn<C>, FetchWidth<C>> =
m.input(ReadyValidArray[InstructionRenameInputInsn[config]][FetchWidth[config]]);
#[hdl]
let start_retire_queue_index: RetireQueueIndex<C> = m.input(RetireQueueIndex[config]);
#[hdl]
let end_retire_queue_index: RetireQueueIndex<C> = m.output(RetireQueueIndex[config]);
#[hdl]
let insns_out: InstructionRenameInsnsOut<C> = m.output(InstructionRenameInsnsOut[config]);
// TODO: handle resetting table after cancelling instructions
#[hdl]
let insns_ready_or_move = wire(ArrayType[Bool][FetchWidth[config]]);
for (insn_ready_or_move, insn_out) in insns_ready_or_move.into_iter().zip(insns_out) {
connect(insn_ready_or_move, insn_out.ready);
}
ArrayVec::for_each(insns_in.data, |fetch_index, input_insn| {
#[hdl]
match input_insn.mop {
UnitMOp::<_, _, _>::TransformedMove(_) => {
connect(insns_ready_or_move[fetch_index], true);
}
UnitMOp::<_, _, _>::AluBranch(_) | UnitMOp::<_, _, _>::LoadStore(_) => {}
}
});
let insns_in_prefix_summary_ty = InsnsInPrefixSummary[config];
#[hdl]
let insns_in_prefix_summaries = wire(ArrayType[insns_in_prefix_summary_ty][FetchWidth[config]]);
let insns_in_prefix_summaries_vec = PrefixSumAlgorithm::WorkEfficient.run(
(0..FetchWidth[config]).map(|fetch_index| {
#[hdl]
let insns_in_prefix_summary_in = wire(insns_in_prefix_summary_ty);
#[hdl]
let InsnsInPrefixSummary::<_> {
all_ready,
ready_count,
retire_queue_used,
config: _,
} = insns_in_prefix_summary_in;
connect(all_ready, insns_out[fetch_index].ready);
connect(
ready_count,
Expr::ty(ready_count).cast_from_uint_unchecked(all_ready.cast_to(UInt[1])),
);
connect(retire_queue_used, Expr::ty(retire_queue_used).zero());
#[hdl]
if let HdlSome(input_insn) = ArrayVec::get(insns_in.data, fetch_index) {
connect(retire_queue_used, ready_count);
#[hdl]
match input_insn.mop {
UnitMOp::<_, _, _>::TransformedMove(_) => {
connect(all_ready, true);
}
UnitMOp::<_, _, _>::AluBranch(_) | UnitMOp::<_, _, _>::LoadStore(_) => {}
}
}
insns_in_prefix_summary_in
}),
|l, r| {
#[hdl]
let insns_in_prefix_summary_merge = wire(insns_in_prefix_summary_ty);
#[hdl]
let InsnsInPrefixSummary::<_> {
all_ready,
ready_count,
retire_queue_used,
config: _,
} = insns_in_prefix_summary_merge;
connect(all_ready, l.all_ready & r.all_ready);
#[hdl]
if l.all_ready {
connect(
ready_count,
Expr::ty(ready_count).cast_from_uint_unchecked(
Length::as_uint(l.ready_count) + Length::as_uint(r.ready_count),
),
);
connect(
retire_queue_used,
Expr::ty(retire_queue_used).cast_from_uint_unchecked(
Length::as_uint(l.retire_queue_used) + Length::as_uint(r.retire_queue_used),
),
);
} else {
connect(ready_count, l.ready_count);
connect(retire_queue_used, l.retire_queue_used);
}
insns_in_prefix_summary_merge
},
);
for (l, r) in insns_in_prefix_summaries
.into_iter()
.zip(insns_in_prefix_summaries_vec)
{
connect(l, r);
}
connect(
insns_in.ready,
insns_in_prefix_summaries[FetchWidth[config] - 1].ready_count,
);
#[hdl]
let retire_queue_indexes = wire(Array[RetireQueueIndex[config]][FetchWidth[config] + 1]);
connect(retire_queue_indexes[0], start_retire_queue_index);
connect(
end_retire_queue_index,
retire_queue_indexes[FetchWidth[config]],
);
for (retire_queue_index, insns_in_prefix_summary) in retire_queue_indexes
.into_iter()
.skip(1)
.zip(insns_in_prefix_summaries)
{
connect_any(
retire_queue_index.index,
start_retire_queue_index.index
+ Length::as_uint(insns_in_prefix_summary.retire_queue_used),
);
}
let mut port_configs = Vec::new();
let mut src_reg_count = 0;
MOpTrait::for_each_src_reg(MOp.uninit(), &mut |_, src_index| {
src_reg_count = src_reg_count.max(src_index + 1);
});
for _ in 0..FetchWidth[config] {
for _ in 0..src_reg_count {
port_configs.push(RenameTablePortConfig::Read {
addr_range: MOpRegNum::NON_CONST_REG_NUMS,
});
}
for dest_reg_kind in MOpDestReg::REG_KINDS {
port_configs.push(RenameTablePortConfig::Write {
addr_range: dest_reg_kind.reg_num_range(),
});
}
}
#[hdl]
let rename_table = instance(rename_table(config, &port_configs));
connect(rename_table.cd, cd);
for read_port in rename_table.read_ports {
connect_any(read_port.addr, 0_hdl_u0);
}
for write_port in rename_table.write_ports {
connect_any(write_port.addr, 0_hdl_u0);
connect_any(write_port.data, PRegNum[config].const_zero());
}
ArrayVec::for_each(
ReadyValidArray::firing_data(insns_in),
|fetch_index, input_insn| {
let read_port_index = fetch_index * src_reg_count;
let write_port_index = fetch_index * MOpDestReg::REG_COUNT;
#[hdl]
let InstructionRenameInputInsn::<_> {
mop,
pc,
renamed_dest,
} = input_insn;
let insn_out =
MOpTrait::map_regs(mop, (), PRegNumWidth[config], &mut |src_reg, src_index| {
connect(
rename_table.read_ports[read_port_index + src_index].addr,
src_reg.cast_bits_to(MOpRegNum),
);
rename_table.read_ports[read_port_index + src_index]
.data
.cast_to_bits()
});
for (i, dest_reg) in MOpDestReg::regs(MOpTrait::dest_reg(mop))
.into_iter()
.enumerate()
{
connect(
rename_table.write_ports[write_port_index + i].addr,
dest_reg,
);
connect(
rename_table.write_ports[write_port_index + i].data,
renamed_dest,
);
}
let insn_out = UnitMOp::try_with_transformed_move_op(
insn_out,
RenamedMOp[PRegNumWidth[config]].TransformedMove,
|insn_out: Expr<HdlOption<_>>, move_reg: Expr<MoveRegMOp<_, _>>| {
for i in 0..MOpDestReg::REG_COUNT {
// execute move by using same PRegNum as src[0] for dest
connect(
rename_table.write_ports[write_port_index + i].data,
move_reg.common.src[0].cast_bits_to(PRegNum[config]),
);
}
// move already executed, so remove it
connect(insn_out, Expr::ty(insn_out).HdlNone());
},
);
connect(
insns_out[fetch_index].data,
HdlOption::map(insn_out, |insn_out| {
#[hdl]
RenamedInsnData::<_, _, _> {
retire_queue_index: retire_queue_indexes[fetch_index],
pc,
dest: renamed_dest,
mop: insn_out,
}
}),
);
},
);
}

View file

@ -1,11 +1,11 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
pub mod config;
pub mod decoder;
pub mod instruction;
pub mod next_pc;
pub mod powerisa_instructions_xml;
pub mod instruction_rename;
pub mod reg_alloc;
pub mod register;
pub mod rename_table;
pub mod retire_queue;
pub mod unit;
pub mod util;

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

@ -3,18 +3,18 @@
use crate::{
config::CpuConfig,
instruction::{
COMMON_MOP_SRC_LEN, MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum,
RenameTableName, UnitOutRegNum,
MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum, UnitOutRegNum,
COMMON_MOP_SRC_LEN,
},
unit::{
GlobalState, TrapData, UnitMOp, UnitOutput, UnitOutputWrite, UnitResult,
UnitResultCompleted, UnitTrait,
unit_base::{UnitForwardingInfo, UnitInput},
},
util::tree_reduce::tree_reduce_with_state,
util::array_vec::ReadyValidArray,
};
use fayalite::{
memory::{WriteStruct, splat_mask},
int::BoolOrIntType,
memory::{splat_mask, WriteStruct},
module::{instance_with_loc, memory_with_loc, wire_with_loc},
prelude::*,
util::ready_valid::ReadyValid,
@ -44,150 +44,12 @@ pub enum FetchDecodeSpecialOp {
#[hdl]
pub struct FetchDecodeInterface<FetchWidth: Size> {
pub decoded_insns: ArrayType<ReadyValid<FetchedDecodedMOp>, FetchWidth>,
pub decoded_insns: ReadyValidArray<FetchedDecodedMOp, FetchWidth>,
#[hdl(flip)]
pub fetch_decode_special_op: ReadyValid<FetchDecodeSpecialOp>,
}
#[hdl]
struct ROBRenamedInsn<UnitNumWidth: Size, OutRegNumWidth: Size> {
mop_dest: MOpDestReg,
p_dest: PRegNum<UnitNumWidth, OutRegNumWidth>,
}
#[hdl]
struct ROBEntry<UnitNumWidth: Size, OutRegNumWidth: Size> {
renamed_insn: ROBRenamedInsn<UnitNumWidth, OutRegNumWidth>,
dest_written: Bool,
}
#[hdl_module]
fn rob(config: &CpuConfig) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let renamed_insns_in: Array<ReadyValid<ROBRenamedInsn<DynSize, DynSize>>> = m.input(
Array[ReadyValid[ROBRenamedInsn[config.unit_num_width()][config.out_reg_num_width]]]
[config.fetch_width.get()],
);
#[hdl]
let unit_forwarding_info: UnitForwardingInfo<DynSize, DynSize, DynSize> =
m.input(config.unit_forwarding_info());
let rob_entry_ty = ROBEntry[config.unit_num_width()][config.out_reg_num_width];
#[hdl]
let rob = reg_builder()
.clock_domain(cd)
.no_reset(Array[rob_entry_ty][config.rob_size.get()]);
#[hdl]
let rob_valid_start = reg_builder()
.clock_domain(cd)
.reset(UInt::range(0..config.rob_size.get()).zero());
#[hdl]
let rob_valid_end = reg_builder()
.clock_domain(cd)
.reset(UInt::range(0..config.rob_size.get()).zero());
#[hdl]
let free_space = wire(UInt::range_inclusive(0..=config.rob_size.get()));
#[hdl]
if rob_valid_end.cmp_lt(rob_valid_start) {
// rob_valid_end wrapped around but start didn't
connect_any(
free_space,
rob_valid_end + config.rob_size.get() - rob_valid_start,
);
} else {
connect_any(free_space, rob_valid_end - rob_valid_start);
}
struct IndexAndRange {
index: Expr<UInt>,
range: std::ops::Range<usize>,
}
let mut next_write_index = IndexAndRange {
index: rob_valid_end,
range: 0..config.rob_size.get(),
};
for fetch_index in 0..config.fetch_width.get() {
let write_index = next_write_index;
let next_write_index_range = write_index.range.start..write_index.range.end + 1;
next_write_index = IndexAndRange {
index: wire_with_loc(
&format!("next_write_index_{fetch_index}"),
SourceLocation::caller(),
UInt::range(next_write_index_range.clone()),
),
range: next_write_index_range,
};
connect(
renamed_insns_in[fetch_index].ready,
fetch_index.cmp_lt(free_space),
);
#[hdl]
if let HdlSome(renamed_insn) = ReadyValid::firing_data(renamed_insns_in[fetch_index]) {
for i in write_index.range.clone() {
#[hdl]
if write_index.index.cmp_eq(i) {
connect(
rob[i % config.rob_size.get()],
#[hdl]
ROBEntry {
renamed_insn,
dest_written: false,
},
);
}
}
}
// TODO: optimize write_index chain better
connect_any(
next_write_index.index,
write_index.index
+ ReadyValid::firing(renamed_insns_in[fetch_index]).cast_to_static::<UInt<1>>(),
);
}
assert!(
config.rob_size >= config.fetch_width,
"rob_size ({}) is too small for fetch_width = {} -- next_write_index would overflow",
config.rob_size,
config.fetch_width,
);
#[hdl]
if next_write_index.index.cmp_lt(config.rob_size.get()) {
connect_any(rob_valid_end, next_write_index.index);
} else {
connect_any(
rob_valid_end,
next_write_index.index - config.rob_size.get(),
);
}
// TODO: optimize better, O(rob_size * unit_count) is too big here
for rob_index in 0..config.rob_size.get() {
for unit_index in 0..config.non_const_unit_nums().len() {
#[hdl]
if let HdlSome(unit_output_write) = unit_forwarding_info.unit_output_writes[unit_index]
{
#[hdl]
let UnitOutputWrite::<_> {
which: unit_out_reg,
value: _,
} = unit_output_write;
let p_reg_num = #[hdl]
PRegNum::<_, _> {
unit_num: config.unit_num().from_index(unit_index),
unit_out_reg,
};
#[hdl]
if rob[rob_index].renamed_insn.p_dest.cmp_eq(p_reg_num) {
connect(rob[rob_index].dest_written, true);
}
}
}
}
}
#[cfg(todo)]
#[hdl_module]
/// combination register allocator, register renaming, unit selection, and retire handling
pub fn reg_alloc(config: &CpuConfig) {
@ -205,10 +67,6 @@ pub fn reg_alloc(config: &CpuConfig) {
);
// TODO: finish
#[hdl]
let rob = instance(rob(config));
connect(rob.cd, cd);
let mut rename_table_mems = BTreeMap::<RenameTableName, MemBuilder<_>>::new();
for reg_kind in MOpDestReg::REG_KINDS {
@ -238,11 +96,6 @@ pub fn reg_alloc(config: &CpuConfig) {
#[hdl]
let renamed_mops_out_reg = wire(Array[HdlOption[config.p_reg_num()]][config.fetch_width.get()]);
for fetch_index in 0..config.fetch_width.get() {
// TODO: finish
connect(
rob.renamed_insns_in[fetch_index].data,
rob.ty().renamed_insns_in.element().data.HdlNone(),
);
// TODO: finish
connect(
fetch_decode_interface.decoded_insns[fetch_index].ready,
@ -263,7 +116,7 @@ pub fn reg_alloc(config: &CpuConfig) {
);
connect(
renamed_mops[fetch_index],
renamed_mops.ty().element().HdlNone(),
Expr::ty(renamed_mops).element().HdlNone(),
);
#[hdl]
struct RenameTableReadPort<T> {
@ -332,7 +185,7 @@ pub fn reg_alloc(config: &CpuConfig) {
let write_port = wire_with_loc(
&format!("{table_name}_{fetch_index}_{}", reg_kind.reg_name()),
SourceLocation::caller(),
write_port_.ty(),
Expr::ty(write_port_),
);
connect(write_port_, write_port);
write_ports.push_back(write_port);
@ -343,7 +196,7 @@ pub fn reg_alloc(config: &CpuConfig) {
addr: 0_hdl_u0,
en: false,
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()),
},
);
@ -375,7 +228,7 @@ pub fn reg_alloc(config: &CpuConfig) {
config.renamed_mop_in_unit().TransformedMove,
|renamed_mop, renamed_move_op: Expr<MoveRegMOp<_, _>>| {
// TODO: finish handling MoveRegMOp
connect(renamed_mop, renamed_mop.ty().HdlNone());
connect(renamed_mop, Expr::ty(renamed_mop).HdlNone());
},
);
connect(
@ -429,7 +282,7 @@ pub fn reg_alloc(config: &CpuConfig) {
);
connect(
selected_unit_index_leaf,
selected_unit_index_leaf.ty().HdlNone(),
Expr::ty(selected_unit_index_leaf).HdlNone(),
);
let unit_index_wire = wire_with_loc(
&format!("unit_index_{fetch_index}_{unit_index}"),
@ -447,7 +300,7 @@ pub fn reg_alloc(config: &CpuConfig) {
let selected_unit_index_node = wire_with_loc(
&format!("selected_unit_index_node_{fetch_index}_{state}"),
SourceLocation::caller(),
l.ty(),
Expr::ty(l),
);
*state += 1;
connect(selected_unit_index_node, l);
@ -483,7 +336,6 @@ pub fn reg_alloc(config: &CpuConfig) {
);
#[hdl]
let unit_forwarding_info = wire(config.unit_forwarding_info());
connect(rob.unit_forwarding_info, unit_forwarding_info);
for (unit_index, unit_config) in config.units.iter().enumerate() {
let dyn_unit = unit_config.kind.unit(config, unit_index);
let unit = instance_with_loc(
@ -516,7 +368,7 @@ pub fn reg_alloc(config: &CpuConfig) {
connect(unit_free_regs_tracker.alloc_out[0].ready, false);
connect(
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() {
#[hdl]
@ -550,7 +402,7 @@ pub fn reg_alloc(config: &CpuConfig) {
} else {
connect(
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(), "");
}
@ -578,8 +430,7 @@ pub fn reg_alloc(config: &CpuConfig) {
connect(unit_to_reg_alloc.unit_forwarding_info, unit_forwarding_info);
connect(
unit_forwarding_info.unit_output_writes[unit_index],
unit_forwarding_info
.ty()
Expr::ty(unit_forwarding_info)
.unit_output_writes
.element()
.HdlNone(),

View file

@ -1,7 +1,10 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::util::tree_reduce::tree_reduce;
use fayalite::{module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid};
use fayalite::{
module::wire_with_loc,
prelude::*,
util::{prefix_sum::reduce, ready_valid::ReadyValid},
};
use std::{num::NonZeroUsize, ops::Range};
#[hdl_module]
@ -44,7 +47,7 @@ pub fn unit_free_regs_tracker(
count,
count_overflowed,
alloc_nums,
}) = tree_reduce(
}) = reduce(
(0..reg_count).map(|index| Summary {
range: index..index + 1,
count: (!allocated_reg[index])
@ -73,7 +76,7 @@ pub fn unit_free_regs_tracker(
let reduced_alloc_nums = wire_with_loc(
&format!("reduced_alloc_nums_{}_{}", range.start, range.end),
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() {
#[hdl]
@ -120,7 +123,10 @@ pub fn unit_free_regs_tracker(
#[cfg(test)]
mod tests {
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;
fn test_unit_free_regs_tracker(
@ -195,7 +201,7 @@ mod tests {
}
}
#[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);
#[hdl]
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
// See Notices.txt for copyright information
use crate::instruction::ConditionMode;
use fayalite::{
expr::CastToImpl,
int::{BoolOrIntType, UIntInRange},
prelude::*,
ty::StaticType,
};
use std::fmt;
use fayalite::prelude::*;
#[hdl]
pub enum FlagsMode {
@ -16,750 +8,131 @@ pub enum FlagsMode {
X86(PRegFlagsX86),
}
trait PRegFlagsViewTraitSealed {
type UnusedInner<T>: AsRef<[T]>
+ 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]
}
}
#[hdl(cmp_eq)]
pub struct PRegFlagsPowerISA {}
impl PRegFlagsPowerISA {
pub fn cr_condition_modes_msb0() -> [Expr<ConditionMode>; 4] {
PRegFlagsPowerISAView::cr_condition_modes()
.into_cr_bits_msb0()
.map(|v| v.expect("known to be Some"))
pub fn xer_ca(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ca_x86_cf
}
pub fn cr_condition_modes_lsb0() -> [Expr<ConditionMode>; 4] {
PRegFlagsPowerISAView::cr_condition_modes()
.into_cr_bits_lsb0()
.map(|v| v.expect("known to be Some"))
pub fn xer_ca32(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ca32_x86_af
}
pub fn cr_condition_modes_msb0_sim() -> [SimValue<ConditionMode>; 4] {
PRegFlagsPowerISAView::cr_condition_modes_sim()
.into_cr_bits_msb0()
.map(|v| v.expect("known to be Some"))
pub fn xer_ov(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ov_x86_of
}
pub fn cr_condition_modes_lsb0_sim() -> [SimValue<ConditionMode>; 4] {
PRegFlagsPowerISAView::cr_condition_modes_sim()
.into_cr_bits_lsb0()
.map(|v| v.expect("known to be Some"))
pub fn xer_ov32(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ov32_x86_df
}
/// 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)]
pub struct PRegFlagsX86 {}
#[hdl(cmp_eq)]
pub struct PRegFlagsX86 {}
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub struct PRegFlagsX86View {
pub unused: ViewUnused([pwr_so]),
pub cf: pwr_ca_x86_cf,
pub zf: pwr_cr_eq_x86_zf,
pub sf: pwr_cr_lt_x86_sf,
pub of: pwr_ov_x86_of,
pub af: pwr_ca32_x86_af,
pub pf: pwr_cr_gt_x86_pf,
pub df: pwr_ov32_x86_df,
impl PRegFlagsX86 {
pub fn cf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_ca_x86_cf
}
pub fn zf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_eq_x86_zf
}
pub fn sf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
flags.to_expr().pwr_cr_lt_x86_sf
}
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 {
(
$(#[$struct_meta:meta])*
$struct_vis:vis struct $PRegFlags:ident<$T:ident: Type = Bool> {
$($field:ident: T,)*
}
) => {
$(#[$struct_meta])*
$struct_vis struct $PRegFlags<$T: Type = Bool> {
$($field: $T,)*
}
struct Fields<$T> {
$($field: $T,)*
}
impl<$T> Fields<$T> {
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)))
}
#[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 {
pwr_ca_x86_cf: Bool,
pwr_ca32_x86_af: Bool,
pwr_ov_x86_of: Bool,
pwr_ov32_x86_df: Bool,
pwr_cr_lt_x86_sf: Bool,
pwr_cr_gt_x86_pf: Bool,
pwr_cr_eq_x86_zf: Bool,
pwr_so: Bool,
}
impl PRegFlags {
/// if trying to set all fields individually, prefer using the individual accessor
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
#[hdl]
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)]

View file

@ -0,0 +1,187 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{CpuConfig, CpuConfigType},
instruction::{MOpRegNum, PRegNum},
util::range_intersection,
};
use fayalite::{
memory::{splat_mask, ReadStruct, WriteStruct},
module::memory_with_loc,
prelude::*,
};
use std::{mem, ops::Range};
#[hdl(no_static)]
pub struct RenameTableReadPort<C: Type + CpuConfigType> {
pub addr: MOpRegNum,
#[hdl(flip)]
pub data: PRegNum<C>,
}
#[hdl(no_static)]
pub struct RenameTableWritePort<C: Type + CpuConfigType> {
pub addr: MOpRegNum,
pub data: PRegNum<C>,
}
#[derive(Clone, Debug)]
pub enum RenameTablePortConfig {
Read { addr_range: Range<u32> },
Write { addr_range: Range<u32> },
}
type C = PhantomConst<CpuConfig>;
/// register rename table.
/// all read/write operations are done in the order of `port_configs`.
/// So if `port_configs[0]` is a write and `port_configs[1]` is a read,
/// then the read port will combinatorially return data written by the
/// write port in the *same* clock cycle. However, if `port_configs[0]`
/// is a read and `port_configs[1]` is a write, then the read port will
/// not see the data written by the write port until the *next* clock cycle.
#[hdl_module]
pub fn rename_table(config: PhantomConst<CpuConfig>, port_configs: &[RenameTablePortConfig]) {
let read_count = port_configs
.iter()
.filter(|v| matches!(v, RenameTablePortConfig::Read { .. }))
.count();
let write_count = port_configs
.iter()
.filter(|v| matches!(v, RenameTablePortConfig::Write { .. }))
.count();
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let read_ports: Array<RenameTableReadPort<C>> =
m.input(Array[RenameTableReadPort[config]][read_count]);
#[hdl]
let write_ports: Array<RenameTableWritePort<C>> =
m.input(Array[RenameTableWritePort[config]][write_count]);
for read_port in read_ports {
connect(read_port.data, PRegNum[config].const_zero());
}
let port_configs_and_indexes = port_configs.iter().scan(
(0usize, 0),
|(read_port_index, write_port_index), port_config| {
Some((
port_config,
match port_config {
RenameTablePortConfig::Read { .. } => {
mem::replace(read_port_index, *read_port_index + 1)
}
RenameTablePortConfig::Write { .. } => {
mem::replace(write_port_index, *write_port_index + 1)
}
},
))
},
);
let mut range_transitions = Vec::with_capacity(port_configs.len() * 2);
for port_config in port_configs {
let (RenameTablePortConfig::Read { addr_range }
| RenameTablePortConfig::Write { addr_range }) = port_config;
range_transitions.push(addr_range.start);
range_transitions.push(addr_range.end);
}
range_transitions.sort_unstable();
range_transitions.dedup();
let mut last_range_transition = None;
for range_transition in range_transitions {
let Some(last_range_transition) = last_range_transition.replace(range_transition) else {
continue;
};
let cur_addr_range = last_range_transition..range_transition;
let mut mem = memory_with_loc(
&if cur_addr_range.len() == 1 {
format!("mem_{:#x}", cur_addr_range.start)
} else {
format!("mem_{:#x}_{:#x}", cur_addr_range.start, cur_addr_range.end)
},
PRegNum[config],
SourceLocation::caller(),
);
mem.depth(cur_addr_range.len());
let addr_in_range = |addr: Expr<MOpRegNum>| {
if cur_addr_range.len() == 1 {
addr.value.cmp_eq(cur_addr_range.start)
} else {
addr.value.cmp_ge(cur_addr_range.start) & addr.value.cmp_lt(cur_addr_range.end)
}
};
for (port_config, port_index) in port_configs_and_indexes.clone() {
match port_config {
RenameTablePortConfig::Read { addr_range } => {
if range_intersection(&addr_range, &cur_addr_range).is_none() {
continue;
}
let port = read_ports[port_index];
#[hdl]
let ReadStruct::<_, _> {
addr,
en,
clk,
data,
} = mem.new_read_port();
connect_any(addr, port.addr.value - cur_addr_range.start);
connect(en, addr_in_range(port.addr));
connect(clk, cd.clk);
#[hdl]
if en {
connect(port.data, data);
}
}
RenameTablePortConfig::Write { addr_range } => {
if range_intersection(&addr_range, &cur_addr_range).is_none() {
continue;
}
let port = write_ports[port_index];
#[hdl]
let WriteStruct::<_, _> {
addr,
en,
clk,
data,
mask,
} = mem.new_write_port();
connect_any(addr, port.addr.value - cur_addr_range.start);
connect(en, addr_in_range(port.addr));
connect(clk, cd.clk);
connect(data, port.data);
connect(mask, splat_mask(Expr::ty(port).data, true.to_expr()));
}
}
}
}
for (port_config_index, (port_config, port_index)) in
port_configs_and_indexes.clone().enumerate()
{
let RenameTablePortConfig::Read { addr_range } = port_config else {
continue;
};
let port = read_ports[port_index];
for (prev_port_config, prev_port_index) in
port_configs_and_indexes.clone().take(port_config_index)
{
let RenameTablePortConfig::Write {
addr_range: prev_addr_range,
} = prev_port_config
else {
continue;
};
if range_intersection(addr_range, prev_addr_range).is_none() {
continue;
}
let prev_port = write_ports[prev_port_index];
#[hdl]
if prev_port.addr.cmp_eq(port.addr) {
connect(port.data, prev_port.data);
}
}
}
}

View file

@ -0,0 +1,33 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{CpuConfig, CpuConfigType},
instruction::{MOpDestReg, PRegNum},
unit::RetireQueueIndex,
};
use fayalite::prelude::*;
#[hdl(no_static)]
pub struct RenameRetireInterface<C: Type + CpuConfigType> {
pub start_retire_queue_index: RetireQueueIndex<C>,
#[hdl(flip)]
pub end_retire_queue_index: RetireQueueIndex<C>,
}
#[hdl(no_static)]
pub struct RetireQueueEntry<C: Type + CpuConfigType> {
pub mop_dest: MOpDestReg,
pub renamed_dest: PRegNum<C>,
}
type C = PhantomConst<CpuConfig>;
#[hdl_module]
pub fn retire_queue(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let rename_retire_interface: RenameRetireInterface<C> = m.output(RenameRetireInterface[config]);
todo!();
}

View file

@ -2,22 +2,21 @@
// See Notices.txt for copyright information
use crate::{
config::CpuConfig,
config::{CpuConfig, CpuConfigType, RetireQueueIndexWidth, UnitCount},
instruction::{
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, UnitOutRegNum,
mop_enum,
mop_enum, AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
RenamedMOp, UnitOutRegNum,
},
register::{FlagsMode, PRegValue},
unit::unit_base::UnitToRegAlloc,
};
use fayalite::{
bundle::{Bundle, BundleType},
int::BoolOrIntType,
intern::{Intern, Interned},
prelude::*,
util::ready_valid::ReadyValid,
};
use serde::{Deserialize, Serialize};
use std::ops::ControlFlow;
pub mod alu_branch;
pub mod unit_base;
@ -48,23 +47,16 @@ macro_rules! all_units {
}
impl $UnitKind {
pub fn unit(self, config: &CpuConfig, unit_index: usize) -> DynUnit {
pub fn unit(self, config: PhantomConst<CpuConfig>, unit_index: usize) -> DynUnit {
match self {
$($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)*
}
}
}
impl ValueType for $UnitKind {
type Type = $HdlUnitKind;
type ValueCategory = fayalite::expr::value_category::ValueCategoryExpr;
fn ty(&self) -> Self::Type {
$HdlUnitKind
}
}
impl ToExpr for $UnitKind {
type Type = $HdlUnitKind;
fn to_expr(&self) -> Expr<Self::Type> {
match self {
$($UnitKind::$Unit => $HdlUnitKind.$Unit(),)*
@ -85,15 +77,7 @@ macro_rules! all_units {
#[impl_mop_into = false]
#[hdl]
$(#[$enum_meta])*
$vis enum $UnitMOpEnum<
$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>,)*
]
> {
$vis enum $UnitMOpEnum<$DestReg: Type, $SrcRegWidth: Size, #[MOp(get_ty = $transformed_move_op_get_ty)] $TransformedMoveOp: Type> {
$(
$(#[$variant_meta])*
$Unit($Op),
@ -116,7 +100,7 @@ macro_rules! all_units {
#[hdl]
$vis fn $extract(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<$Op>> {
let expr = expr.to_expr();
let ty = expr.ty();
let ty = Expr::ty(expr);
#[hdl]
let $extract = wire(HdlOption[ty.$Unit]);
connect($extract, HdlOption[ty.$Unit].HdlNone());
@ -182,10 +166,10 @@ macro_rules! all_units {
$TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
{
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]
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
let mut connect_transformed_move_op = Some(connect_transformed_move_op);
#[hdl]
@ -222,23 +206,28 @@ macro_rules! all_units {
})*
};
$(impl<$DestReg: Type, $SrcRegWidth: Size> MOpInto<RenamedMOp<$DestReg, $SrcRegWidth>> for $BeforeOp {
fn mop_into_ty(self) -> RenamedMOp<$DestReg, $SrcRegWidth> {
RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)]
}
fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> {
MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(this.ty()).$BeforeUnit(this)
}
})*
const _: () = {
#[hdl]
type $DestReg = ();
$(impl<$DestReg: Type, $SrcRegWidth: Size> MOpInto<RenamedMOp<$DestReg, $SrcRegWidth>> for $AfterOp {
fn mop_into_ty(self) -> RenamedMOp<$DestReg, $SrcRegWidth> {
RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)]
}
fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> {
MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(this.ty()).$AfterUnit(this)
}
})*
$(impl<$SrcRegWidth: Size> MOpInto<RenamedMOp<$SrcRegWidth>> for $BeforeOp {
fn mop_into_ty(self) -> RenamedMOp<$SrcRegWidth> {
RenamedMOp[MOpTrait::src_reg_width(self)]
}
fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$SrcRegWidth>> {
MOpInto::<RenamedMOp<$SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$BeforeUnit(this)
}
})*
$(impl<$SrcRegWidth: Size> MOpInto<RenamedMOp<$SrcRegWidth>> for $AfterOp {
fn mop_into_ty(self) -> RenamedMOp<$SrcRegWidth> {
RenamedMOp[MOpTrait::src_reg_width(self)]
}
fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$SrcRegWidth>> {
MOpInto::<RenamedMOp<$SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$AfterUnit(this)
}
})*
};
};
}
@ -271,18 +260,80 @@ pub struct GlobalState {
pub flags_mode: FlagsMode,
}
/// index into the retire queue (the queue of instructions that haven't yet retired)
#[hdl(cmp_eq, no_static)]
pub struct RetireQueueIndex<C: Type + CpuConfigType> {
/// increases by one for each instruction added to the retire queue.
///
/// this wraps around, so you must not compare it using `cmp_lt`/`cmp_gt`
/// but instead must use [`Self::insns_until`] and compare the output with zero.
pub index: UIntType<RetireQueueIndexWidth<C>>,
pub config: C,
}
impl<C: Type + CpuConfigType> RetireQueueIndex<C> {
pub fn insns_until(
this: impl ToExpr<Type = Self>,
target: impl ToExpr<Type = Self>,
) -> Expr<SIntType<RetireQueueIndexWidth<C>>> {
let this = this.to_expr();
let target = target.to_expr();
assert_eq!(Expr::ty(this), Expr::ty(target));
(this.index - target.index).cast_to(Expr::ty(this).index.as_same_width_sint())
}
}
#[hdl(no_static)]
pub struct RenamedInsnData<C: Type + CpuConfigType, MOp, DestReg> {
pub retire_queue_index: RetireQueueIndex<C>,
pub pc: UInt<64>,
pub dest: DestReg,
pub mop: MOp,
}
#[hdl(no_static)]
pub struct UnitForwardingInfo<C: Type + CpuConfigType> {
pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<C>>, UnitCount<C>>,
pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<C>>, UnitCount<C>>,
}
#[hdl(no_static)]
pub struct UnitToRegAlloc<C: Type + CpuConfigType, MOp: Type, ExtraOut: Type> {
#[hdl(flip)]
pub unit_forwarding_info: UnitForwardingInfo<C>,
#[hdl(flip)]
pub input: ReadyValid<RenamedInsnData<C, MOp, UnitOutRegNum<C>>>,
#[hdl(flip)]
pub cancel_input: HdlOption<UnitCancelInput<C>>,
pub output: HdlOption<UnitOutput<C, ExtraOut>>,
pub ready_for_retire_queue_index: HdlOption<RetireQueueIndex<C>>,
}
impl<C: Type + CpuConfigType, MOp: Type, ExtraOut: Type> UnitToRegAlloc<C, MOp, ExtraOut> {
pub fn mop_ty(self) -> MOp {
self.input.data.HdlSome.mop
}
pub fn extra_out_ty(self) -> ExtraOut {
self.output.HdlSome.extra_out_ty()
}
}
#[hdl(cmp_eq)]
pub struct UnitResultCompleted<ExtraOut> {
pub value: PRegValue,
pub extra_out: ExtraOut,
}
#[hdl(cmp_eq)]
pub struct UnitOutputWrite<OutRegNumWidth: Size> {
pub which: UnitOutRegNum<OutRegNumWidth>,
#[hdl(cmp_eq, no_static)]
pub struct UnitOutputWrite<C: Type + CpuConfigType> {
pub dest: UnitOutRegNum<C>,
pub value: PRegValue,
}
#[hdl]
pub type UnitOutputWrites<C: Type + CpuConfigType> =
ArrayType<HdlOption<UnitOutputWrite<C>>, UnitCount<C>>;
#[hdl(cmp_eq)]
pub struct TrapData {
// TODO
@ -300,21 +351,31 @@ impl<ExtraOut: Type> UnitResult<ExtraOut> {
}
}
#[hdl]
pub struct UnitOutput<OutRegNumWidth: Size, ExtraOut> {
pub which: UnitOutRegNum<OutRegNumWidth>,
#[hdl(no_static)]
pub struct UnitOutput<C: Type + CpuConfigType, ExtraOut> {
pub dest: UnitOutRegNum<C>,
pub retire_queue_index: RetireQueueIndex<C>,
pub result: UnitResult<ExtraOut>,
}
impl<OutRegNumWidth: Size, ExtraOut: Type> UnitOutput<OutRegNumWidth, ExtraOut> {
impl<C: Type + CpuConfigType, ExtraOut: Type> UnitOutput<C, ExtraOut> {
pub fn extra_out_ty(self) -> ExtraOut {
self.result.extra_out_ty()
}
}
#[hdl(cmp_eq)]
pub struct UnitCancelInput<OutRegNumWidth: Size> {
pub which: UnitOutRegNum<OutRegNumWidth>,
#[hdl(cmp_eq, no_static)]
pub struct UnitCancelInput<C: Type + CpuConfigType> {
pub target: RetireQueueIndex<C>,
}
impl<C: Type + CpuConfigType> UnitCancelInput<C> {
pub fn is_canceled(
this: impl ToExpr<Type = Self>,
insn_retire_queue_index: impl ToExpr<Type = RetireQueueIndex<C>>,
) -> Expr<Bool> {
RetireQueueIndex::insns_until(insn_retire_queue_index, this.to_expr().target).cmp_ge(0i8)
}
}
pub trait UnitTrait:
@ -330,17 +391,14 @@ pub trait UnitTrait:
fn unit_kind(&self) -> UnitKind;
fn extract_mop(
&self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>>;
fn extract_mop(&self, mop: Expr<RenamedMOp<DynSize>>) -> Expr<HdlOption<Self::MOp>>;
fn module(&self) -> Interned<Module<Self::Type>>;
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>>;
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>>;
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>;
@ -388,10 +446,7 @@ impl UnitTrait for DynUnit {
self.unit_kind
}
fn extract_mop(
&self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>> {
fn extract_mop(&self, mop: Expr<RenamedMOp<DynSize>>) -> Expr<HdlOption<Self::MOp>> {
self.unit.extract_mop(mop)
}
@ -402,7 +457,7 @@ impl UnitTrait for DynUnit {
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
self.unit.unit_to_reg_alloc(this)
}
@ -443,10 +498,7 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T
self.0.unit_kind()
}
fn extract_mop(
&self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>> {
fn extract_mop(&self, mop: Expr<RenamedMOp<DynSize>>) -> Expr<HdlOption<Self::MOp>> {
Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop)))
}
@ -457,7 +509,7 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
Expr::from_bundle(Expr::as_bundle(
self.0.unit_to_reg_alloc(Expr::from_bundle(this)),
))

View file

@ -2,33 +2,26 @@
// See Notices.txt for copyright information
use crate::{
config::CpuConfig,
config::{CpuConfig, PRegNumWidth},
instruction::{
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOpDefaultImm,
CompareMOp, LogicalFlagsMOp, LogicalMOp, MOpTrait, OutputIntegerMode, ReadSpecialMOp,
RenamedMOp, ShiftRotateMOp, UnitOutRegNum,
},
register::{
FlagsMode, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, PRegFlagsX86,
PRegFlagsX86View, PRegValue, ViewUnused,
AddSubMOp, AluBranchMOp, AluCommonMOp, CommonMOp, LogicalMOp, OutputIntegerMode,
RenamedMOp, COMMON_MOP_SRC_LEN,
},
register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue},
unit::{
unit_base::{unit_base, ExecuteEnd, ExecuteStart},
DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult,
UnitResultCompleted, UnitTrait,
unit_base::{ExecuteEnd, ExecuteStart, UnitToRegAlloc, unit_base},
UnitResultCompleted, UnitToRegAlloc, UnitTrait,
},
};
use fayalite::{
intern::{Intern, Interned},
module::wire_with_loc,
prelude::*,
util::ready_valid::ReadyValid,
intern::Interned, module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid,
};
use std::{collections::HashMap, ops::RangeTo};
#[hdl]
fn add_sub<SrcCount: KnownSize>(
mop: Expr<AddSubMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
mop: Expr<AddSubMOp<(), DynSize, SrcCount>>,
pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
@ -42,11 +35,11 @@ fn add_sub<SrcCount: KnownSize>(
add_pc,
} = mop;
#[hdl]
let AluCommonMOp::<_, _, _, _> {
let AluCommonMOp::<_, _, _> {
common,
output_integer_mode,
} = 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]
let carry_in_before_inversion = wire();
connect(carry_in_before_inversion, false);
@ -60,13 +53,13 @@ fn add_sub<SrcCount: KnownSize>(
FlagsMode::PowerISA(_) => {
connect(
carry_in_before_inversion,
PRegFlagsPowerISA::view(src_values[1].flags).xer_ca,
PRegFlagsPowerISA::xer_ca(src_values[1].flags),
);
}
FlagsMode::X86(_) => {
connect(
carry_in_before_inversion,
PRegFlagsX86::view(src_values[1].flags).cf,
PRegFlagsX86::cf(src_values[1].flags),
);
}
}
@ -203,36 +196,27 @@ fn add_sub<SrcCount: KnownSize>(
#[hdl]
match flags_mode {
FlagsMode::PowerISA(_) => {
connect(
flags,
PRegFlagsPowerISA::from_view(PRegFlagsPowerISAView {
unused: ViewUnused::splat(false.to_expr()),
xer_ca: pwr_ca,
xer_ca32: pwr_ca32,
xer_ov: pwr_ov,
xer_ov32: pwr_ov32,
so: pwr_so,
cr_lt: pwr_cr_lt,
cr_gt: pwr_cr_gt,
cr_eq: pwr_cr_eq,
}),
);
PRegFlagsPowerISA::clear_unused(flags);
connect(PRegFlagsPowerISA::xer_ca(flags), pwr_ca);
connect(PRegFlagsPowerISA::xer_ca32(flags), pwr_ca32);
connect(PRegFlagsPowerISA::xer_ov(flags), pwr_ov);
connect(PRegFlagsPowerISA::xer_ov32(flags), pwr_ov32);
connect(PRegFlagsPowerISA::cr_lt(flags), pwr_cr_lt);
connect(PRegFlagsPowerISA::cr_gt(flags), pwr_cr_gt);
connect(PRegFlagsPowerISA::cr_eq(flags), pwr_cr_eq);
connect(PRegFlagsPowerISA::so(flags), pwr_so);
}
FlagsMode::X86(_) => {
connect(
flags,
PRegFlagsX86::from_view(PRegFlagsX86View {
unused: ViewUnused::splat(false.to_expr()),
cf: x86_cf,
zf: x86_zf,
sf: x86_sf,
of: x86_of,
af: x86_af,
pf: x86_pf,
// this insn doesn't write DF, so it's output isn't used for reading DF
df: false.to_expr(),
}),
);
PRegFlagsX86::clear_unused(flags);
connect(PRegFlagsX86::cf(flags), x86_cf);
connect(PRegFlagsX86::af(flags), x86_af);
connect(PRegFlagsX86::of(flags), x86_of);
connect(PRegFlagsX86::sf(flags), x86_sf);
connect(PRegFlagsX86::pf(flags), x86_pf);
connect(PRegFlagsX86::zf(flags), x86_zf);
// this insn doesn't write DF, so it's output isn't used for reading DF
connect(PRegFlagsX86::df(flags), false);
}
}
#[hdl]
@ -243,95 +227,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]
fn logical(
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<2>>>,
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>>,
mop: Expr<LogicalMOp<(), DynSize>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
@ -344,20 +242,15 @@ fn read_special(
}
#[hdl_module]
pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let unit_to_reg_alloc: UnitToRegAlloc<
AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>,
PhantomConst<CpuConfig>,
AluBranchMOp<(), DynSize>,
(),
DynSize,
DynSize,
DynSize,
> = m.output(config.unit_to_reg_alloc(
AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()],
(),
));
> = m.output(UnitToRegAlloc[config][AluBranchMOp[()][PRegNumWidth[config]]][()]);
#[hdl]
let global_state: GlobalState = m.input();
@ -365,23 +258,22 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
let unit_base = instance(unit_base(
config,
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_base.cd, cd);
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]
if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) {
#[hdl]
let ExecuteStart::<_> {
mop,
pc,
src_values,
} = execute_start;
let ExecuteStart::<_, _> { insn, src_values } = execute_start;
#[hdl]
match mop {
match insn.mop {
AluBranchMOp::<_, _>::AddSub(mop) => connect(
unit_base.execute_end,
HdlSome(
@ -389,10 +281,11 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
dest: insn.dest,
retire_queue_index: insn.retire_queue_index,
result: UnitResult[()].Completed(add_sub(
mop,
pc,
insn.pc,
global_state.flags_mode,
src_values,
)),
@ -407,27 +300,11 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
dest: insn.dest,
retire_queue_index: insn.retire_queue_index,
result: UnitResult[()].Completed(add_sub(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
},
),
),
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,
insn.pc,
global_state.flags_mode,
src_values,
)),
@ -442,7 +319,8 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
dest: insn.dest,
retire_queue_index: insn.retire_queue_index,
result: UnitResult[()].Completed(logical(
mop,
global_state.flags_mode,
@ -452,142 +330,20 @@ 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,
)),
},
},
),
),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct AluBranch {
config: Interned<CpuConfig>,
config: PhantomConst<CpuConfig>,
module: Interned<Module<alu_branch>>,
}
impl AluBranch {
pub fn new(config: &CpuConfig, unit_index: usize) -> Self {
pub fn new(config: PhantomConst<CpuConfig>, unit_index: usize) -> Self {
Self {
config: config.intern(),
config,
module: alu_branch(config, unit_index),
}
}
@ -596,7 +352,7 @@ impl AluBranch {
impl UnitTrait for AluBranch {
type Type = alu_branch;
type ExtraOut = ();
type MOp = AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>;
type MOp = AluBranchMOp<(), DynSize>;
fn ty(&self) -> Self::Type {
self.module.io_ty()
@ -614,10 +370,7 @@ impl UnitTrait for AluBranch {
UnitKind::AluBranch
}
fn extract_mop(
&self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>> {
fn extract_mop(&self, mop: Expr<RenamedMOp<DynSize>>) -> Expr<HdlOption<Self::MOp>> {
UnitMOp::alu_branch_mop(mop)
}
@ -628,7 +381,7 @@ impl UnitTrait for AluBranch {
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
this.unit_to_reg_alloc
}

View file

@ -2,72 +2,31 @@
// See Notices.txt for copyright information
use crate::{
config::CpuConfig,
instruction::{COMMON_MOP_SRC_LEN, MOpTrait, PRegNum, UnitNum, UnitOutRegNum},
config::{CpuConfig, CpuConfigType, UnitOutRegNumWidth},
instruction::{MOpTrait, PRegNum, UnitNum, UnitOutRegNum, COMMON_MOP_SRC_LEN},
register::PRegValue,
unit::{UnitCancelInput, UnitOutput, UnitOutputWrite},
util::tree_reduce::tree_reduce,
unit::{
RenamedInsnData, RetireQueueIndex, UnitCancelInput, UnitForwardingInfo, UnitOutput,
UnitOutputWrite, UnitToRegAlloc,
},
};
use fayalite::{
memory::splat_mask,
module::{memory_with_loc, wire_with_loc},
prelude::*,
ty::StaticType,
util::ready_valid::ReadyValid,
util::{prefix_sum::reduce, ready_valid::ReadyValid},
};
use std::marker::PhantomData;
#[hdl]
pub struct UnitForwardingInfo<UnitNumWidth: Size, OutRegNumWidth: Size, UnitCount: Size> {
pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<OutRegNumWidth>>, UnitCount>,
pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<OutRegNumWidth>>, UnitCount>,
pub _phantom: PhantomData<UnitNumWidth>,
}
#[hdl]
pub struct UnitInput<MOp: Type> {
pub mop: MOp,
pub pc: UInt<64>,
}
#[hdl]
pub struct UnitToRegAlloc<
MOp: Type,
ExtraOut: Type,
UnitNumWidth: Size,
OutRegNumWidth: Size,
UnitCount: Size,
> {
#[hdl(flip)]
pub unit_forwarding_info: UnitForwardingInfo<UnitNumWidth, OutRegNumWidth, UnitCount>,
#[hdl(flip)]
pub input: ReadyValid<UnitInput<MOp>>,
#[hdl(flip)]
pub cancel_input: HdlOption<UnitCancelInput<OutRegNumWidth>>,
pub output: HdlOption<UnitOutput<OutRegNumWidth, ExtraOut>>,
}
impl<MOp: Type, ExtraOut: Type, UnitNumWidth: Size, OutRegNumWidth: Size, UnitCount: Size>
UnitToRegAlloc<MOp, ExtraOut, UnitNumWidth, OutRegNumWidth, UnitCount>
{
pub fn mop_ty(self) -> MOp {
self.input.data.HdlSome.mop
}
pub fn extra_out_ty(self) -> ExtraOut {
self.output.HdlSome.extra_out_ty()
}
}
#[hdl]
pub struct ExecuteStart<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>> {
pub mop: MOp,
pub pc: UInt<64>,
#[hdl(no_static)]
pub struct ExecuteStart<C: Type + CpuConfigType, MOp: Type> {
pub insn: RenamedInsnData<C, MOp, UnitOutRegNum<C>>,
pub src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>,
}
#[hdl]
pub struct ExecuteEnd<OutRegNumWidth: Size, ExtraOut> {
pub unit_output: UnitOutput<OutRegNumWidth, ExtraOut>,
#[hdl(no_static)]
pub struct ExecuteEnd<C: Type + CpuConfigType, ExtraOut> {
pub unit_output: UnitOutput<C, ExtraOut>,
}
#[hdl]
@ -147,27 +106,34 @@ impl InFlightOpState {
}
}
#[hdl]
struct InFlightOp<MOp: Type> {
#[hdl(no_static)]
struct InFlightOp<C: Type + CpuConfigType, MOp: Type> {
state: InFlightOpState,
mop: MOp,
pc: UInt<64>,
insn: RenamedInsnData<C, MOp, UnitOutRegNum<C>>,
src_ready_flags: Array<Bool, { COMMON_MOP_SRC_LEN }>,
}
#[hdl]
struct InFlightOpsSummary<OpIndexWidth: Size> {
empty_op_index: HdlOption<UIntType<OpIndexWidth>>,
ready_op_index: HdlOption<UIntType<OpIndexWidth>>,
impl<C: Type + CpuConfigType, MOp: Type> InFlightOp<C, MOp> {
fn config(self) -> C {
self.insn.retire_queue_index.config
}
}
impl<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> {
#[hdl(no_static)]
struct InFlightOpsSummary<C: Type + CpuConfigType, OpIndexWidth: Size> {
empty_op_index: HdlOption<UIntType<OpIndexWidth>>,
ready_op_index: HdlOption<UIntType<OpIndexWidth>>,
ready_for_retire_queue_index: HdlOption<RetireQueueIndex<C>>,
}
impl<C: Type + CpuConfigType, OpIndexWidth: Size> InFlightOpsSummary<C, OpIndexWidth> {
#[hdl]
fn new<MOp: Type>(
op_index: usize,
op_index_ty: UIntType<OpIndexWidth>,
in_flight_op: impl ToExpr<Type = HdlOption<InFlightOp<MOp>>>,
in_flight_op: impl ToExpr<Type = HdlOption<InFlightOp<C, MOp>>>,
) -> Expr<Self> {
let in_flight_op = in_flight_op.to_expr();
let empty_op_index = wire_with_loc(
&format!("empty_op_index_{op_index}"),
SourceLocation::caller(),
@ -180,34 +146,73 @@ impl<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> {
HdlOption[op_index_ty],
);
connect(ready_op_index, HdlOption[op_index_ty].HdlNone());
let ready_for_retire_queue_index = wire_with_loc(
&format!("ready_for_retire_queue_index_{op_index}"),
SourceLocation::caller(),
HdlOption[RetireQueueIndex[Expr::ty(in_flight_op).HdlSome.config()]],
);
connect(
ready_for_retire_queue_index,
Expr::ty(ready_for_retire_queue_index).HdlNone(),
);
#[hdl]
if let HdlSome(in_flight_op) = in_flight_op {
#[hdl]
let InFlightOp::<_> {
let InFlightOp::<_, _> {
state,
mop: _,
pc: _,
insn,
src_ready_flags,
} = in_flight_op;
connect(ready_op_index, HdlOption[op_index_ty].HdlNone());
let ready_for_retire_queue_index_value = wire_with_loc(
&format!("ready_for_retire_queue_index_value_{op_index}"),
SourceLocation::caller(),
RetireQueueIndex[Expr::ty(in_flight_op).config()],
);
connect(ready_for_retire_queue_index_value, insn.retire_queue_index);
// TODO: don't subtract one from `.index` when instruction is ready to
// do something at retire time (e.g. non-speculative stores)
// -- that will need a new InFlightOpState variant.
connect_any(
ready_for_retire_queue_index_value.index,
// subtract one -- this instruction isn't ready to retire,
// but the previous one could be
insn.retire_queue_index.index - 1u8,
);
#[hdl]
match state {
InFlightOpState::Ready =>
{
InFlightOpState::Ready => {
#[hdl]
if src_ready_flags.cmp_eq([true; COMMON_MOP_SRC_LEN]) {
connect(ready_op_index, HdlSome(op_index.cast_to(op_index_ty)));
}
connect(
ready_for_retire_queue_index,
HdlSome(ready_for_retire_queue_index_value),
);
}
InFlightOpState::CanceledAndRunning => {
// the instruction has been canceled, but is still
// executing so we treat it as if it doesn't exist
// other than making sure the in_flight_op slot
// isn't reused until execution is done.
}
InFlightOpState::Running => {
connect(
ready_for_retire_queue_index,
HdlSome(ready_for_retire_queue_index_value),
);
}
InFlightOpState::CanceledAndRunning | InFlightOpState::Running => {}
}
} else {
connect(empty_op_index, HdlSome(op_index.cast_to(op_index_ty)));
}
#[hdl]
InFlightOpsSummary::<_> {
InFlightOpsSummary::<_, _> {
empty_op_index,
ready_op_index,
ready_for_retire_queue_index,
}
}
#[hdl]
@ -215,22 +220,60 @@ impl<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> {
let l = l.to_expr();
let r = r.to_expr();
#[hdl]
InFlightOpsSummary::<_> {
let combine_ready_for_retire_queue_index = wire(Expr::ty(l).ready_for_retire_queue_index);
#[hdl]
if let HdlSome(l_ready_for_retire_queue_index) = l.ready_for_retire_queue_index {
#[hdl]
if let HdlSome(r_ready_for_retire_queue_index) = r.ready_for_retire_queue_index {
#[hdl]
if RetireQueueIndex::insns_until(
l_ready_for_retire_queue_index,
r_ready_for_retire_queue_index,
)
.cmp_lt(0i8)
{
connect(
combine_ready_for_retire_queue_index,
l.ready_for_retire_queue_index,
);
} else {
connect(
combine_ready_for_retire_queue_index,
r.ready_for_retire_queue_index,
);
}
} else {
connect(
combine_ready_for_retire_queue_index,
l.ready_for_retire_queue_index,
);
}
} else {
connect(
combine_ready_for_retire_queue_index,
r.ready_for_retire_queue_index,
);
}
#[hdl]
InFlightOpsSummary::<_, _> {
empty_op_index: HdlOption::or(l.empty_op_index, r.empty_op_index),
ready_op_index: HdlOption::or(l.ready_op_index, r.ready_op_index),
ready_for_retire_queue_index: combine_ready_for_retire_queue_index,
}
}
}
impl InFlightOpsSummary<DynSize> {
impl InFlightOpsSummary<PhantomConst<CpuConfig>, DynSize> {
fn summarize<MOp: Type, MaxInFlight: Size>(
in_flight_ops: impl ToExpr<Type = ArrayType<HdlOption<InFlightOp<MOp>>, MaxInFlight>>,
in_flight_ops: impl ToExpr<
Type = ArrayType<HdlOption<InFlightOp<PhantomConst<CpuConfig>, MOp>>, MaxInFlight>,
>,
) -> Expr<Self> {
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_ty = UInt::range(index_range.clone());
tree_reduce(
reduce(
index_range.map(|i| Self::new(i, index_ty, in_flight_ops[i])),
Self::combine,
)
@ -239,11 +282,8 @@ impl InFlightOpsSummary<DynSize> {
}
#[hdl_module]
pub fn unit_base<
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>,
ExtraOut: Type,
>(
config: &CpuConfig,
pub fn unit_base<MOp: Type + MOpTrait<DestReg = (), SrcRegWidth = DynSize>, ExtraOut: Type>(
config: PhantomConst<CpuConfig>,
unit_index: usize,
mop_ty: MOp,
extra_out_ty: ExtraOut,
@ -251,18 +291,19 @@ pub fn unit_base<
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let unit_to_reg_alloc: UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> =
m.output(config.unit_to_reg_alloc(mop_ty, extra_out_ty));
let unit_to_reg_alloc: UnitToRegAlloc<PhantomConst<CpuConfig>, MOp, ExtraOut> =
m.output(UnitToRegAlloc[config][mop_ty][extra_out_ty]);
#[hdl]
let execute_start: ReadyValid<ExecuteStart<MOp>> = m.output(ReadyValid[ExecuteStart[mop_ty]]);
let execute_start: ReadyValid<ExecuteStart<PhantomConst<CpuConfig>, MOp>> =
m.output(ReadyValid[ExecuteStart[config][mop_ty]]);
#[hdl]
let execute_end: HdlOption<ExecuteEnd<DynSize, ExtraOut>> =
m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][extra_out_ty]]);
let execute_end: HdlOption<ExecuteEnd<PhantomConst<CpuConfig>, ExtraOut>> =
m.input(HdlOption[ExecuteEnd[config][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 in_flight_op_ty = InFlightOp[mop_ty];
let max_in_flight = config.get().unit_max_in_flight(unit_index).get();
let in_flight_op_ty = InFlightOp[config][mop_ty];
#[hdl]
let in_flight_ops = reg_builder()
.clock_domain(cd)
@ -270,7 +311,7 @@ pub fn unit_base<
let in_flight_ops_summary_value = InFlightOpsSummary::summarize(in_flight_ops);
#[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(
@ -278,17 +319,21 @@ pub fn unit_base<
HdlOption::is_some(in_flight_ops_summary.empty_op_index),
);
connect(
unit_to_reg_alloc.ready_for_retire_queue_index,
in_flight_ops_summary.ready_for_retire_queue_index,
);
#[hdl]
let UnitForwardingInfo::<_, _, _> {
let UnitForwardingInfo::<_> {
unit_output_writes,
unit_reg_frees,
_phantom: _,
} = unit_to_reg_alloc.unit_forwarding_info;
#[hdl]
let read_src_regs = wire(mop_ty.src_regs_ty());
connect(
read_src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
);
#[hdl]
let read_src_values = wire();
@ -297,33 +342,33 @@ pub fn unit_base<
let input_src_regs = wire(mop_ty.src_regs_ty());
connect(
input_src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
);
#[hdl]
let input_src_regs_valid = wire();
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| {
let mut mem = memory_with_loc(
&format!("unit_{unit_index}_output_regs_valid"),
Bool,
SourceLocation::caller(),
);
mem.depth(1 << config.out_reg_num_width);
mem.depth(1 << UnitOutRegNumWidth[config]);
mem
})
.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(
&format!("unit_{unit_index}_output_regs"),
PRegValue,
SourceLocation::caller(),
);
unit_output_regs.depth(1 << config.out_reg_num_width);
unit_output_regs.depth(1 << UnitOutRegNumWidth[config]);
for src_index in 0..COMMON_MOP_SRC_LEN {
let read_port = unit_output_regs.new_read_port();
let p_reg_num = read_src_regs[src_index].cast_bits_to(config.p_reg_num());
let p_reg_num = read_src_regs[src_index].cast_bits_to(PRegNum[config]);
connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
connect(read_port.en, false);
connect(read_port.clk, cd.clk);
@ -336,7 +381,7 @@ pub fn unit_base<
for src_index in 0..COMMON_MOP_SRC_LEN {
let read_port = unit_output_regs_valid[unit_index].new_read_port();
let p_reg_num = input_src_regs[src_index].cast_bits_to(config.p_reg_num());
let p_reg_num = input_src_regs[src_index].cast_bits_to(PRegNum[config]);
connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
connect(read_port.en, false);
connect(read_port.clk, cd.clk);
@ -361,15 +406,15 @@ pub fn unit_base<
connect(ready_write_port.mask, true);
#[hdl]
if let HdlSome(unit_output_write) = unit_output_writes[unit_index] {
connect_any(write_port.addr, unit_output_write.which.value);
connect_any(write_port.addr, unit_output_write.dest.value);
connect(write_port.data, unit_output_write.value);
connect(write_port.en, true);
connect_any(ready_write_port.addr, unit_output_write.which.value);
connect_any(ready_write_port.addr, unit_output_write.dest.value);
connect(ready_write_port.en, true);
let p_reg_num = #[hdl]
PRegNum::<_, _> {
unit_num: config.unit_num().from_index(unit_index),
unit_out_reg: unit_output_write.which,
PRegNum::<_> {
unit_num: UnitNum[config].from_index(unit_index),
unit_out_reg: unit_output_write.dest,
};
for src_index in 0..COMMON_MOP_SRC_LEN {
#[hdl]
@ -399,9 +444,8 @@ pub fn unit_base<
execute_start.data,
HdlSome(
#[hdl]
ExecuteStart::<_> {
mop: in_flight_op.mop,
pc: in_flight_op.pc,
ExecuteStart::<_, _> {
insn: in_flight_op.insn,
src_values: read_src_values,
},
),
@ -411,7 +455,7 @@ pub fn unit_base<
connect(
unit_to_reg_alloc.output,
unit_to_reg_alloc.output.ty().HdlNone(),
Expr::ty(unit_to_reg_alloc.output).HdlNone(),
);
#[hdl]
@ -420,12 +464,17 @@ pub fn unit_base<
#[hdl]
if let HdlSome(input) = ReadyValid::firing_data(unit_to_reg_alloc.input) {
#[hdl]
let UnitInput::<_> { mop, pc } = input;
let RenamedInsnData::<_, _, _> {
retire_queue_index,
pc: _,
dest: _,
mop,
} = input;
#[hdl]
let input_mop_src_regs = wire(mop_ty.src_regs_ty());
connect(
input_mop_src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
);
MOp::connect_src_regs(mop, input_mop_src_regs);
let src_ready_flags = wire_with_loc(
@ -436,20 +485,24 @@ pub fn unit_base<
connect(src_ready_flags, input_src_regs_valid);
connect(input_src_regs, input_mop_src_regs);
#[hdl]
if unit_to_reg_alloc.cancel_input.cmp_ne(HdlSome(
#[hdl]
UnitCancelInput::<_> {
which: MOp::dest_reg(mop),
},
)) {
let input_is_canceled = wire();
connect(input_is_canceled, false);
#[hdl]
if let HdlSome(cancel_input) = unit_to_reg_alloc.cancel_input {
connect(
input_is_canceled,
UnitCancelInput::is_canceled(cancel_input, retire_queue_index),
);
}
#[hdl]
if !input_is_canceled {
connect(
input_in_flight_op,
HdlSome(
#[hdl]
InFlightOp::<_> {
InFlightOp::<_, _> {
state: InFlightOpState.Ready(),
mop,
pc,
insn: input,
src_ready_flags,
},
),
@ -483,13 +536,11 @@ pub fn unit_base<
#[hdl]
if let HdlSome(in_flight_op) = in_flight_ops[in_flight_op_index] {
#[hdl]
let InFlightOp::<_> {
let InFlightOp::<_, _> {
state,
mop,
pc,
insn,
src_ready_flags,
} = in_flight_op;
let which = MOp::dest_reg(mop);
let src_regs = wire_with_loc(
&format!("in_flight_op_src_regs_{in_flight_op_index}"),
SourceLocation::caller(),
@ -497,13 +548,13 @@ pub fn unit_base<
);
connect(
src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
);
MOp::connect_src_regs(mop, src_regs);
MOp::connect_src_regs(insn.mop, src_regs);
#[hdl]
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);
}
@ -512,17 +563,17 @@ pub fn unit_base<
in_flight_op_next_src_ready_flags[in_flight_op_index],
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]
if let HdlSome(unit_output_write) = unit_output_writes[unit_index] {
#[hdl]
let UnitOutputWrite::<_> {
which: unit_out_reg,
dest: unit_out_reg,
value: _,
} = unit_output_write;
let p_reg_num = #[hdl]
PRegNum::<_, _> {
unit_num: config.unit_num().from_index(unit_index),
PRegNum::<_> {
unit_num: UnitNum[config].from_index(unit_index),
unit_out_reg,
};
for src_index in 0..COMMON_MOP_SRC_LEN {
@ -537,20 +588,21 @@ pub fn unit_base<
}
}
connect(
in_flight_op_canceling[in_flight_op_index],
unit_to_reg_alloc.cancel_input.cmp_eq(HdlSome(
#[hdl]
UnitCancelInput::<_> { which },
)),
);
connect(in_flight_op_canceling[in_flight_op_index], false);
#[hdl]
if let HdlSome(cancel_input) = unit_to_reg_alloc.cancel_input {
connect(
in_flight_op_canceling[in_flight_op_index],
UnitCancelInput::is_canceled(cancel_input, insn.retire_queue_index),
);
}
#[hdl]
if let HdlSome(execute_end) = execute_end {
#[hdl]
let ExecuteEnd::<_, _> { unit_output } = execute_end;
#[hdl]
if which.cmp_eq(unit_output.which) {
if insn.dest.cmp_eq(unit_output.dest) {
connect(in_flight_op_execute_ending[in_flight_op_index], true);
#[hdl]
if !in_flight_op_canceling[in_flight_op_index] {
@ -567,7 +619,7 @@ pub fn unit_base<
#[hdl]
if let HdlSome(execute_start) = ReadyValid::firing_data(execute_start) {
#[hdl]
if which.cmp_eq(MOp::dest_reg(execute_start.mop)) {
if insn.dest.cmp_eq(execute_start.insn.dest) {
connect(in_flight_op_execute_starting[in_flight_op_index], true);
}
}
@ -594,10 +646,9 @@ pub fn unit_base<
in_flight_ops[in_flight_op_index],
HdlSome(
#[hdl]
InFlightOp::<_> {
InFlightOp::<_, _> {
state,
mop,
pc,
insn,
src_ready_flags: in_flight_op_next_src_ready_flags[in_flight_op_index],
},
),

View file

@ -1,11 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// 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 tree_reduce;
pub(crate) const fn range_u32_len(range: &std::ops::Range<u32>) -> usize {
let retval = range.end.saturating_sub(range.start);
@ -29,254 +25,15 @@ pub(crate) const fn range_u32_nth_or_panic(range: &std::ops::Range<u32>, index:
}
}
// 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
pub(crate) const fn range_intersection(
a: &std::ops::Range<u32>,
b: &std::ops::Range<u32>,
) -> Option<std::ops::Range<u32>> {
let start = if a.start > b.start { a.start } else { b.start };
let end = if a.end < b.end { a.end } else { b.end };
if start < end {
Some(start..end)
} else {
None
}
}

View file

@ -1,10 +1,150 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::{expr::ops::ExprIndex, int::UIntInRangeInclusiveType, prelude::*};
use fayalite::{
expr::{
ops::{ExprCastTo, ExprIndex, ExprPartialEq, ExprPartialOrd},
ToLiteralBits,
},
int::{IntType, SizeType},
intern::{Intern, Interned},
prelude::*,
ty::{MatchVariantWithoutScope, StaticType, TypeProperties},
};
use std::{marker::PhantomData, ops::Index};
#[hdl]
pub type Length<Max: Size> = UIntInRangeInclusiveType<ConstUsize<0>, Max>;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Length<Max: Size> {
ty: UInt,
_phantom: PhantomData<Max>,
}
impl<Max: Size> Length<Max> {
pub fn new(max: Max::SizeType) -> Self {
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<Max: Size, DestWidth: Size> ExprCastTo<UIntType<DestWidth>> for Length<Max> {
fn cast_to(src: Expr<Self>, to_type: UIntType<DestWidth>) -> Expr<UIntType<DestWidth>> {
Expr::<UInt>::from_canonical(Expr::canonical(src)).cast_to(to_type)
}
}
#[allow(non_upper_case_globals)]
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
#[hdl]
@ -19,31 +159,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
#[hdl]
ArrayVec {
elements: self.elements.uninit(),
len: 0u8.cast_to(self.len),
}
}
#[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),
len: self.len.zero(),
}
}
pub fn element(self) -> T {
@ -63,8 +179,8 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
let elements = elements.to_expr();
let len = len.to_expr();
assert_eq!(
Length[N::from_usize(elements.ty().len())],
len.ty(),
Length::new(N::from_usize(Expr::ty(elements).len())),
Expr::ty(len),
"len type mismatch",
);
#[hdl]
@ -76,12 +192,9 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
pub fn len(this: impl ToExpr<Type = Self>) -> Expr<Length<N>> {
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> {
let len = Self::len(this);
len.cmp_eq(0u8)
len.cmp_eq(Expr::ty(len).zero())
}
pub fn capacity(self) -> usize {
self.elements.len()
@ -97,77 +210,11 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
let this = this.to_expr();
for (index, element) in this.elements.into_iter().enumerate() {
#[hdl]
if index.cmp_lt(this.len) {
if index.cmp_lt(Length::as_uint(this.len)) {
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> {
ArrayVec {
elements: ArrayType[new_element_ty][N::from_usize(self.elements.len())],
@ -182,7 +229,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
) -> Expr<ArrayVec<U, N>> {
let this = this.to_expr();
#[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);
Self::for_each(this, |index, element| {
connect(mapped_array_vec[index], f(index, element));
@ -190,48 +237,44 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
mapped_array_vec
}
#[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>> {
let this = this.to_expr();
#[hdl]
let array_vec_as_array_of_options =
wire(ArrayType[HdlOption[this.ty().element()]][N::from_usize(this.ty().capacity())]);
let array_vec_as_array_of_options = wire(
ArrayType[HdlOption[Expr::ty(this).element()]]
[N::from_usize(Expr::ty(this).capacity())],
);
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| {
connect(array_vec_as_array_of_options[index], HdlSome(element))
});
array_vec_as_array_of_options
}
#[hdl]
pub fn get<Idx: IntType<Dyn = UInt>>(
this: impl ToExpr<Type = Self>,
index: impl ToExpr<Type = Idx>,
) -> Expr<HdlOption<T>> {
let this = this.to_expr();
let index = Expr::as_dyn_int(index.to_expr());
let never_in_bounds = index.cmp_ge(Expr::ty(this).capacity());
if let Ok(never_in_bounds) = never_in_bounds.to_literal_bits() {
if never_in_bounds[0] {
// avoid error from out-of-bounds constant index
return HdlOption[Expr::ty(this).element()].HdlNone();
}
}
#[hdl]
let array_vec_get = wire(HdlOption[Expr::ty(this).element()]);
connect(array_vec_get, Expr::ty(array_vec_get).HdlNone());
#[hdl]
if index.cmp_lt(Length::as_uint(Self::len(this))) {
connect(array_vec_get, HdlSome(this.elements[index]));
}
array_vec_get
}
}
impl<T: Type, N: Size, Idx, IdxWidth: Size> ExprIndex<Idx> for ArrayVec<T, N>
@ -247,33 +290,34 @@ where
}
}
impl<T: Type> ArrayVec<T, ConstUsize<1>> {
#[hdl]
pub struct ReadyValidArray<T: Type, N: Size> {
pub data: ArrayVec<T, N>,
#[hdl(flip)]
pub ready: Length<N>,
}
impl<T: Type, N: Size> ReadyValidArray<T, N> {
#[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),
pub fn firing_len(this: impl ToExpr<Type = Self>) -> Expr<Length<N>> {
let this = this.to_expr();
assert_eq!(Expr::ty(this).data.len_ty(), Expr::ty(this).ready);
#[hdl]
let firing_len = wire(Expr::ty(this).data.len);
connect(firing_len, this.data.len);
#[hdl]
if this.data.len.cmp_gt(this.ready) {
connect(firing_len, this.ready);
}
firing_len
}
#[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)
}
pub fn firing_data(this: impl ToExpr<Type = Self>) -> Expr<ArrayVec<T, N>> {
let this = this.to_expr();
#[hdl]
let firing_data = wire(Expr::ty(this).data);
connect(firing_data, this.data);
connect(firing_data.len, Self::firing_len(this));
firing_data
}
}

View file

@ -1,152 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum TreeReduceOp {
Input,
Reduce,
}
#[derive(Copy, Clone, Debug)]
struct Entry {
start: usize,
depth: u32,
}
#[derive(Clone, Debug)]
pub struct TreeReduceOps {
len: usize,
stack: Vec<Entry>,
}
impl TreeReduceOps {
pub fn new(len: usize) -> Self {
TreeReduceOps {
len,
stack: Vec::new(),
}
}
}
impl Iterator for TreeReduceOps {
type Item = TreeReduceOp;
fn next(&mut self) -> Option<Self::Item> {
match *self.stack {
[] if self.len != 0 => {
self.stack.push(Entry { start: 0, depth: 0 });
Some(TreeReduceOp::Input)
}
[.., ref mut second_last, last] if second_last.depth == last.depth => {
second_last.depth += 1;
self.stack.pop();
Some(TreeReduceOp::Reduce)
}
[.., last] if self.len - last.start > 1 << last.depth => {
let start = last.start + (1 << last.depth);
self.stack.push(Entry { start, depth: 0 });
Some(TreeReduceOp::Input)
}
[.., ref mut second_last, _] => {
second_last.depth += 1;
self.stack.pop();
Some(TreeReduceOp::Reduce)
}
_ => None,
}
}
}
#[track_caller]
pub fn tree_reduce_with_state<S, I, R>(
iter: impl IntoIterator<IntoIter: ExactSizeIterator, Item = I>,
state: &mut S,
mut input: impl FnMut(&mut S, I) -> R,
mut reduce: impl FnMut(&mut S, R, R) -> R,
) -> Option<R> {
let mut stack = Vec::new();
let mut iter = iter.into_iter();
for op in TreeReduceOps::new(iter.len()) {
match op {
TreeReduceOp::Input => stack.push(input(
state,
iter.next().expect("inconsistent iterator len() and next()"),
)),
TreeReduceOp::Reduce => {
let Some(r) = stack.pop() else {
unreachable!();
};
let Some(l) = stack.pop() else {
unreachable!();
};
stack.push(reduce(state, l, r));
}
}
}
stack.pop()
}
pub fn tree_reduce<T>(
iter: impl IntoIterator<Item = T, IntoIter: ExactSizeIterator>,
mut reduce: impl FnMut(T, T) -> T,
) -> Option<T> {
tree_reduce_with_state(iter, &mut (), |_, v| v, move |_, l, r| reduce(l, r))
}
#[cfg(test)]
mod tests {
use super::*;
use std::ops::Range;
fn recursive_tree_reduce(range: Range<usize>, ops: &mut Vec<TreeReduceOp>) {
if range.len() == 1 {
ops.push(TreeReduceOp::Input);
return;
}
if range.is_empty() {
return;
}
let pow2_len = range.len().next_power_of_two();
let split = range.start + pow2_len / 2;
recursive_tree_reduce(range.start..split, ops);
recursive_tree_reduce(split..range.end, ops);
ops.push(TreeReduceOp::Reduce);
}
#[test]
fn test_tree_reduce() {
const EXPECTED: &'static [&'static [TreeReduceOp]] = {
use TreeReduceOp::{Input as I, Reduce as R};
&[
&[],
&[I],
&[I, I, R],
&[I, I, R, I, R],
&[I, I, R, I, I, R, R],
&[I, I, R, I, I, R, R, I, R],
&[I, I, R, I, I, R, R, I, I, R, R],
&[I, I, R, I, I, R, R, I, I, R, I, R, R],
&[I, I, R, I, I, R, R, I, I, R, I, I, R, R, R],
]
};
for len in 0..64 {
let mut expected = vec![];
recursive_tree_reduce(0..len, &mut expected);
if let Some(&expected2) = EXPECTED.get(len) {
assert_eq!(*expected, *expected2, "len={len}");
}
assert_eq!(
TreeReduceOps::new(len).collect::<Vec<_>>(),
expected,
"len={len}"
);
let seq: Vec<_> = (0..len).collect();
assert_eq!(
seq,
tree_reduce(seq.iter().map(|&v| vec![v]), |mut l, r| {
l.extend_from_slice(&r);
l
})
.unwrap_or_default()
);
}
}
}

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,16 +1,20 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![cfg(todo)]
use cpu::{
config::{CpuConfig, UnitConfig},
instruction::{AddSubMOp, LogicalMOp, Lut4, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode},
reg_alloc::{FetchedDecodedMOp, reg_alloc},
instruction::{AddSubMOp, LogicalMOp, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode},
reg_alloc::{reg_alloc, FetchedDecodedMOp},
register::{FlagsMode, PRegFlagsPowerISA},
unit::{GlobalState, UnitKind},
};
use fayalite::{
assert_export_firrtl,
firrtl::ExportOptions,
prelude::*,
sim::{Simulation, time::SimDuration, vcd::VcdWriterDecls},
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation},
util::RcWriter,
};
use std::num::NonZeroUsize;
@ -59,7 +63,7 @@ fn test_reg_alloc() {
[HdlSome(()), HdlNone()],
},
[0u8; 2],
0x12345678u32.cast_to_static::<SInt<_>>(),
0x12345678u32.cast_to_static(),
OutputIntegerMode.DupLow32(),
false,
false,
@ -81,7 +85,7 @@ fn test_reg_alloc() {
[HdlSome(()), HdlNone()],
},
[1u8, 0, 0],
1.cast_to_static::<SInt<_>>(),
1.cast_to_static(),
OutputIntegerMode.Full64(),
false,
false,
@ -99,9 +103,9 @@ fn test_reg_alloc() {
flag_regs: [HdlNone(), HdlSome(())],
},
[2u8, 4u8],
0.cast_to_static::<SInt<_>>(),
0.cast_to_static(),
OutputIntegerMode.Full64(),
Lut4::from_fn(|a, b| a ^ b),
0b0110_hdl_u4,
),
];
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

@ -0,0 +1,20 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# See Notices.txt for copyright information
[package]
name = "name_mangling_serde"
description = "serde serializer/deserializer for name mangling"
workspace = "../.."
readme = "README.md"
publish = false
edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
version.workspace = true
[dependencies]
serde.workspace = true
serde_json.workspace = true
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(todo)'] }

View file

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

View file

@ -0,0 +1 @@
../../Notices.txt

View file

@ -0,0 +1,470 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use serde::{de::DeserializeOwned, Serialize};
use serde_json::{Map, Number, Value};
use std::{
fmt::{self, Write},
num::ParseIntError,
};
macro_rules! byte_enum {
(
#[repr(u8)]
$(#[$meta:meta])*
$vis:vis enum $enum:ident {
$($Variant:ident = $value:expr,)*
}
) => {
#[repr(u8)]
$(#[$meta])*
$vis enum $enum {
$($Variant = $value,)*
}
impl $enum {
$vis fn new(v: u8) -> Option<Self> {
struct Values;
#[allow(non_upper_case_globals)]
impl Values {
$(const $Variant: u8 = $enum::$Variant as u8;)*
}
match v {
$(Values::$Variant => Some(Self::$Variant),)*
_ => None,
}
}
#[allow(dead_code)]
$vis fn as_char(self) -> char {
const {
$(assert!((Self::$Variant as u8).is_ascii());)*
};
self as u8 as char
}
}
};
}
macro_rules! string_escapes {
(
$key_vis:vis enum $StringEscapeKey:ident {}
$value_vis:vis enum $StringEscapeValue:ident {
$(
#[key = $key:expr]
$Variant:ident = $value:expr,
)*
}
) => {
byte_enum! {
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
$key_vis enum $StringEscapeKey {
$($Variant = $key,)*
}
}
byte_enum! {
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
$value_vis enum $StringEscapeValue {
$($Variant = $value,)*
}
}
impl From<$StringEscapeKey> for $StringEscapeValue {
fn from(v: $StringEscapeKey) -> Self {
match v {
$($StringEscapeKey::$Variant => Self::$Variant,)*
}
}
}
impl From<$StringEscapeValue> for $StringEscapeKey {
fn from(v: $StringEscapeValue) -> Self {
match v {
$($StringEscapeValue::$Variant => Self::$Variant,)*
}
}
}
};
}
string_escapes! {
enum StringEscapeKey {}
enum StringEscapeValue {
#[key = b's']
Space = b' ',
#[key = b't']
Tab = b'\t',
#[key = b'r']
CR = b'\r',
#[key = b'n']
NewLine = b'\n',
#[key = b'_']
Underline = b'_',
}
}
fn json_string_to_name_part(value: &str, out: &mut String) {
out.push(ValuePrefix::String.as_char());
write!(out, "{}_", value.len()).unwrap();
for b in value.bytes() {
if let Some(v) = StringEscapeValue::new(b) {
out.push('_');
out.push(StringEscapeKey::from(v).as_char());
} else if b.is_ascii_alphanumeric() {
out.push(b as char);
} else {
write!(out, "_{b:02x}").unwrap()
}
}
}
byte_enum! {
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
enum ValuePrefix {
Null = b'z',
False = b'f',
True = b't',
Number = b'n',
String = b's',
Array = b'a',
Object = b'o',
}
}
fn json_value_to_name_part(value: &Value, out: &mut String) {
match value {
Value::Null => out.push(ValuePrefix::Null.as_char()),
Value::Bool(false) => out.push(ValuePrefix::False.as_char()),
Value::Bool(true) => out.push(ValuePrefix::True.as_char()),
Value::Number(number) => {
out.push(ValuePrefix::Number.as_char());
let start = out.len();
write!(out, "{number}").unwrap();
for i in start..out.len() {
out.replace_range(
i..=i,
match out.as_bytes()[i] {
b'0'..=b'9' => continue,
b'+' => "",
b'-' => "n",
b'.' => "p",
b'e' | b'E' => "e",
_ => unreachable!("invalid character in JSON number"),
},
);
}
}
Value::String(string) => json_string_to_name_part(string, out),
Value::Array(array) => {
out.push(ValuePrefix::Array.as_char());
write!(out, "{}", array.len()).unwrap();
for element in array {
json_value_to_name_part(element, out);
}
}
Value::Object(object) => {
out.push(ValuePrefix::Object.as_char());
write!(out, "{}", object.len()).unwrap();
for (k, v) in object {
json_string_to_name_part(k, out);
json_value_to_name_part(v, out);
}
}
}
}
pub const NAME_PREFIX: &str = "__HDL";
pub fn json_value_to_name(value: &Value) -> String {
let mut retval = NAME_PREFIX.into();
json_value_to_name_part(value, &mut retval);
retval
}
#[derive(Debug)]
pub enum Error {
Serde(serde_json::Error),
NameDoesNotStartWithKnownPrefix,
UnknownValuePrefix,
MissingValuePrefix,
InvalidLength(ParseIntError),
TrailingCharacters,
KeyMustBeAString,
StringMissingUnderline,
StringTruncated,
InvalidEscape,
InvalidString,
}
impl From<serde_json::Error> for Error {
fn from(value: serde_json::Error) -> Self {
Self::Serde(value)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Serde(e) => e.fmt(f),
Self::NameDoesNotStartWithKnownPrefix => {
f.write_str("name does not start with the known prefix")
}
Self::UnknownValuePrefix => f.write_str("unknown value prefix"),
Self::MissingValuePrefix => f.write_str("missing value prefix"),
Self::InvalidLength(_) => f.write_str("invalid length"),
Self::TrailingCharacters => f.write_str("trailing characters"),
Self::KeyMustBeAString => f.write_str("key must be a string"),
Self::StringMissingUnderline => f.write_str("string missing `_` after length"),
Self::StringTruncated => f.write_str("string truncated"),
Self::InvalidEscape => f.write_str("invalid escape"),
Self::InvalidString => f.write_str("invalid string"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Serde(e) => e.source(),
Self::NameDoesNotStartWithKnownPrefix => None,
Self::UnknownValuePrefix => None,
Self::MissingValuePrefix => None,
Self::InvalidLength(e) => Some(e),
Self::TrailingCharacters => None,
Self::KeyMustBeAString => None,
Self::StringMissingUnderline => None,
Self::StringTruncated => None,
Self::InvalidEscape => None,
Self::InvalidString => None,
}
}
}
struct NameParser<'a> {
name_part: &'a str,
number_buf: String,
}
impl NameParser<'_> {
fn parse_len(&mut self) -> Result<usize, Error> {
let len_end = self
.name_part
.bytes()
.position(|b| !b.is_ascii_digit())
.unwrap_or(self.name_part.len());
let (len, rest) = self.name_part.split_at(len_end);
self.name_part = rest;
len.parse().map_err(Error::InvalidLength)
}
fn parse_string_without_prefix(&mut self) -> Result<String, Error> {
let len = self.parse_len()?;
let Some(rest) = self.name_part.strip_prefix("_") else {
return Err(Error::StringMissingUnderline);
};
self.name_part = rest;
let mut bytes = Vec::new();
for _ in 0..len {
let b = self
.name_part
.bytes()
.next()
.ok_or(Error::StringTruncated)?;
if b.is_ascii_alphanumeric() {
bytes.push(b);
self.name_part = &self.name_part[1..];
} else if b == b'_' {
self.name_part = &self.name_part[1..];
let escape = self.name_part.bytes().next().ok_or(Error::InvalidEscape)?;
self.name_part = &self.name_part[1..];
if let Some(high) = (escape as char).to_digit(16) {
let low = self
.name_part
.bytes()
.next()
.ok_or(Error::StringTruncated)?;
let low = (low as char).to_digit(16).ok_or(Error::InvalidString)?;
self.name_part = &self.name_part[1..];
bytes.push((high * 16 + low) as u8);
} else {
let escape = StringEscapeKey::new(escape).ok_or(Error::InvalidEscape)?;
bytes.push(StringEscapeValue::from(escape) as u8);
}
} else if let Some(high) = (b as char).to_digit(16) {
self.name_part = &self.name_part[1..];
let low = self
.name_part
.bytes()
.next()
.ok_or(Error::StringTruncated)?;
let low = (low as char).to_digit(16).ok_or(Error::InvalidString)?;
self.name_part = &self.name_part[1..];
bytes.push((high * 16 + low) as u8);
} else {
return Err(Error::InvalidString);
}
}
String::from_utf8(bytes).map_err(|_| Error::InvalidString)
}
fn parse_string(&mut self) -> Result<String, Error> {
if let ValuePrefix::String = self.parse_value_prefix()? {
self.parse_string_without_prefix()
} else {
Err(Error::KeyMustBeAString)
}
}
fn parse_number_without_prefix(&mut self) -> Result<Number, Error> {
let mut bytes = self.name_part.as_bytes().iter();
self.number_buf.clear();
if let Some(b'n') = bytes.clone().next() {
bytes.next();
self.number_buf.push('-');
}
while let Some(&b @ b'0'..=b'9') = bytes.clone().next() {
bytes.next();
self.number_buf.push(b as char);
}
if let Some(b'p') = bytes.clone().next() {
bytes.next();
self.number_buf.push('.');
while let Some(&b @ b'0'..=b'9') = bytes.clone().next() {
bytes.next();
self.number_buf.push(b as char);
}
}
if let Some(b'e') = bytes.clone().next() {
bytes.next();
self.number_buf.push('e');
if let Some(b'n') = bytes.clone().next() {
bytes.next();
self.number_buf.push('-');
}
while let Some(&b @ b'0'..=b'9') = bytes.clone().next() {
bytes.next();
self.number_buf.push(b as char);
}
}
self.name_part = &self.name_part[self.name_part.len() - bytes.len()..];
Ok(self.number_buf.parse()?)
}
fn parse_value_prefix(&mut self) -> Result<ValuePrefix, Error> {
let value_prefix = self
.name_part
.bytes()
.next()
.ok_or(Error::MissingValuePrefix)?;
let value_prefix = ValuePrefix::new(value_prefix).ok_or(Error::UnknownValuePrefix)?;
self.name_part = &self.name_part[1..];
Ok(value_prefix)
}
fn parse_value(&mut self) -> Result<Value, Error> {
Ok(match self.parse_value_prefix()? {
ValuePrefix::Null => Value::Null,
ValuePrefix::False => Value::Bool(false),
ValuePrefix::True => Value::Bool(true),
ValuePrefix::Number => Value::Number(self.parse_number_without_prefix()?),
ValuePrefix::String => Value::String(self.parse_string_without_prefix()?),
ValuePrefix::Array => {
let len = self.parse_len()?;
let mut array = Vec::new();
for _ in 0..len {
array.push(self.parse_value()?);
}
Value::Array(array)
}
ValuePrefix::Object => {
let len = self.parse_len()?;
let mut object = Map::new();
for _ in 0..len {
let key = self.parse_string()?;
let value = self.parse_value()?;
object.insert(key, value);
}
Value::Object(object)
}
})
}
}
pub fn name_to_json_value(name: &str) -> Result<Value, Error> {
let Some(name_part) = name.strip_prefix(NAME_PREFIX) else {
return Err(Error::NameDoesNotStartWithKnownPrefix);
};
let mut parser = NameParser {
name_part,
number_buf: String::new(),
};
let retval = parser.parse_value()?;
if !parser.name_part.is_empty() {
Err(Error::TrailingCharacters)
} else {
Ok(retval)
}
}
pub fn from_name<T: DeserializeOwned>(name: &str) -> Result<T, Error> {
Ok(serde_json::from_value(name_to_json_value(name)?)?)
}
pub fn to_name<T: Serialize>(value: T) -> Result<String, Error> {
Ok(json_value_to_name(&serde_json::to_value(value)?))
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn test_from_to_name() {
#[track_caller]
fn check_from_to_name(value: Value, name: &str) {
assert_eq!(name, json_value_to_name(&value));
assert_eq!(
Ok(value),
name_to_json_value(name).map_err(|e| e.to_string())
);
}
check_from_to_name(json! { null }, "__HDLz");
check_from_to_name(json! { false }, "__HDLf");
check_from_to_name(json! { true }, "__HDLt");
check_from_to_name(json! { 0 }, "__HDLn0");
check_from_to_name(json! { 0.1 }, "__HDLn0p1");
check_from_to_name(json! { -0.1 }, "__HDLnn0p1");
check_from_to_name(json! { 1234567 }, "__HDLn1234567");
check_from_to_name(json! { -1.2345678e-20 }, "__HDLnn1p2345678en20");
check_from_to_name(json! { -1.2345e300 }, "__HDLnn1p2345e300");
check_from_to_name(json! { -5 }, "__HDLnn5");
check_from_to_name(json! { "" }, "__HDLs0_");
check_from_to_name(json! { "a" }, "__HDLs1_a");
check_from_to_name(json! { "A" }, "__HDLs1_A");
check_from_to_name(json! { "z" }, "__HDLs1_z");
check_from_to_name(json! { "Z" }, "__HDLs1_Z");
check_from_to_name(json! { "0" }, "__HDLs1_0");
check_from_to_name(json! { "9" }, "__HDLs1_9");
check_from_to_name(json! { "_" }, "__HDLs1___");
check_from_to_name(json! { " " }, "__HDLs1__s");
check_from_to_name(json! { "\t" }, "__HDLs1__t");
check_from_to_name(json! { "\r" }, "__HDLs1__r");
check_from_to_name(json! { "\n" }, "__HDLs1__n");
check_from_to_name(json! { "\u{25}" }, "__HDLs1__25");
check_from_to_name(json! { "\u{100}" }, "__HDLs2__c4_80");
check_from_to_name(json! { "\u{1000}" }, "__HDLs3__e1_80_80");
check_from_to_name(json! { "\u{10000}" }, "__HDLs4__f0_90_80_80");
check_from_to_name(json! { "foo" }, "__HDLs3_foo");
check_from_to_name(json! { { "foo": 123 } }, "__HDLo1s3_foon123");
check_from_to_name(
json! { { "foo": 123, "bar": null } },
"__HDLo2s3_foon123s3_barz",
);
check_from_to_name(json! { [1, 2, 3, 4] }, "__HDLa4n1n2n3n4");
check_from_to_name(
json! { { "a": [], "b": null, "c": 1234, "d": {} } },
"__HDLo4s1_aa0s1_bzs1_cn1234s1_do0",
);
}
}

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"$')
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"$')
MERMAID_HEADER=('^"%% SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"%% See Notices.txt for copyright information"$')
function main()
{
@ -46,15 +45,12 @@ function main()
*/LICENSE.md|*/Notices.txt)
# 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
;;
/.forgejo/workflows/*.yml|*/.gitignore|*/.gitattributes|*.toml)
/.forgejo/workflows/*.yml|*/.gitignore|*.toml)
check_file "$file" "${POUND_HEADER[@]}"
;;
*.mermaid)
check_file "$file" "${MERMAID_HEADER[@]}"
;;
*.md)
check_file "$file" "${MD_HEADER[@]}"
;;