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
27 changed files with 2208 additions and 1134 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' }}

176
Cargo.lock generated
View file

@ -1,6 +1,18 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
version = 3
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
@ -81,12 +93,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "2.6.0"
@ -166,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"
@ -210,6 +207,8 @@ name = "cpu"
version = "0.1.0"
dependencies = [
"fayalite",
"name_mangling_serde",
"serde",
]
[[package]]
@ -303,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#edcc5927a5f9ebca6df5720bb1f5931e50095a57"
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",
@ -330,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#edcc5927a5f9ebca6df5720bb1f5931e50095a57"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8"
dependencies = [
"fayalite-proc-macros-impl",
]
@ -338,7 +335,7 @@ dependencies = [
[[package]]
name = "fayalite-proc-macros-impl"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#edcc5927a5f9ebca6df5720bb1f5931e50095a57"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8"
dependencies = [
"base16ct",
"num-bigint",
@ -353,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#edcc5927a5f9ebca6df5720bb1f5931e50095a57"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8"
dependencies = [
"indexmap",
"prettyplease",
@ -371,12 +368,6 @@ version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[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"
@ -395,14 +386,13 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.3.4"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
"wasi",
]
[[package]]
@ -410,16 +400,9 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"ahash",
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
@ -450,7 +433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
"hashbrown",
"serde",
]
@ -468,16 +451,16 @@ 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",
"libc",
"scopeguard",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@ -498,6 +481,14 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "name_mangling_serde"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
@ -533,26 +524,22 @@ 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 = "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]]
@ -583,37 +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 = "rustix"
version = "0.38.37"
@ -776,13 +738,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "which"
@ -796,12 +755,6 @@ dependencies = [
"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"
@ -820,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"
@ -899,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"
@ -913,3 +851,23 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -7,14 +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]
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" }
serde = { version = "1.0.202", features = ["derive"] }
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,3 +16,8 @@ version.workspace = true
[dependencies]
fayalite.workspace = true
serde.workspace = true
name_mangling_serde.workspace = true
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(todo)'] }

View file

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

View file

@ -1,16 +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::prelude::*;
use fayalite::{
intern::{Intern, Interned},
prelude::*,
};
use serde::{Deserialize, Serialize};
use std::num::NonZeroUsize;
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UnitConfig {
pub kind: UnitKind,
@ -27,15 +28,14 @@ impl UnitConfig {
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[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,
/// default value for [`UnitConfig::max_in_flight`]
pub default_unit_max_in_flight: NonZeroUsize,
pub rob_size: NonZeroUsize,
}
impl CpuConfig {
@ -52,13 +52,12 @@ impl CpuConfig {
};
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,
default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT,
rob_size,
}
}
pub fn non_const_unit_nums(&self) -> std::ops::Range<usize> {
@ -67,53 +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 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
}
}
mod sealed {
pub trait Sealed {}
}
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,11 +1,14 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{unit::UnitMOp, util::range_u32_len};
use crate::{
config::{CpuConfigType, UnitNumWidth, UnitOutRegNumWidth},
unit::UnitMOp,
util::range_u32_len,
};
use fayalite::{
expr::ops::{ArrayLiteral, ExprPartialEq},
intern::Interned,
prelude::*,
sim::value::SimValuePartialEq,
};
use std::{fmt, marker::PhantomData, ops::Range};
@ -26,7 +29,10 @@ impl<T: MOpTrait> MOpInto<T> for T {
}
pub trait MOpTrait: Type {
type Mapped<NewDestReg: Type, NewSrcRegWidth: Size>: MOpTrait<DestReg = NewDestReg, SrcRegWidth = NewSrcRegWidth>;
type Mapped<NewDestReg: Type, NewSrcRegWidth: Size>: MOpTrait<
DestReg = NewDestReg,
SrcRegWidth = NewSrcRegWidth,
>;
type DestReg: Type;
type SrcRegWidth: Size;
fn dest_reg_ty(self) -> Self::DestReg;
@ -71,11 +77,11 @@ pub trait CommonMOpTrait: MOpTrait {
type PrefixPad: KnownSize;
type SrcCount: KnownSize;
type CommonMOpTraitMapped<NewDestReg: Type, NewSrcRegWidth: Size>: CommonMOpTrait<
DestReg = NewDestReg,
SrcRegWidth = NewSrcRegWidth,
PrefixPad = Self::PrefixPad,
SrcCount = Self::SrcCount,
>;
DestReg = NewDestReg,
SrcRegWidth = NewSrcRegWidth,
PrefixPad = Self::PrefixPad,
SrcCount = Self::SrcCount,
>;
type CommonMOpTraitDestReg: Type;
type CommonMOpTraitSrcRegWidth: Size;
fn common_mop_ty(
@ -182,12 +188,6 @@ impl ExprPartialEq<Self> for OutputIntegerMode {
}
}
impl SimValuePartialEq<Self> for OutputIntegerMode {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
SimValue::opaque(this) == SimValue::opaque(other)
}
}
pub const MOP_IMM_WIDTH: usize = 34;
pub const MOP_MIN_REG_WIDTH: usize = 8;
pub const COMMON_MOP_SRC_LEN: usize = 3;
@ -809,19 +809,21 @@ common_mop_struct! {
}
}
#[hdl(cmp_eq)]
#[hdl(cmp_eq, no_static)]
/// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind.
/// zero is used for built-in constants, such as the zero register
pub struct UnitNum<Width: Size> {
pub adj_value: UIntType<Width>,
pub struct UnitNum<C: Type + CpuConfigType> {
pub adj_value: UIntType<UnitNumWidth<C>>,
pub config: C,
}
impl<Width: Size> UnitNum<Width> {
impl<C: Type + CpuConfigType> UnitNum<C> {
#[hdl]
pub fn const_zero(self) -> Expr<Self> {
#[hdl]
UnitNum {
adj_value: CONST_ZERO_UNIT_NUM.cast_to(self.adj_value),
config: self.config,
}
}
#[hdl]
@ -829,6 +831,7 @@ impl<Width: Size> UnitNum<Width> {
#[hdl]
UnitNum {
adj_value: (index + 1).cast_to(self.adj_value),
config: self.config,
}
}
pub fn is_index(expr: impl ToExpr<Type = Self>, index: usize) -> Expr<Bool> {
@ -839,7 +842,7 @@ impl<Width: Size> UnitNum<Width> {
.cmp_eq(expr.adj_value)
}
#[hdl]
pub fn as_index(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<UIntType<Width>>> {
pub fn as_index(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<UIntType<DynSize>>> {
let expr = expr.to_expr();
#[hdl]
let unit_index = wire(HdlOption[Expr::ty(expr).adj_value]);
@ -857,19 +860,20 @@ impl<Width: Size> UnitNum<Width> {
pub const CONST_ZERO_UNIT_NUM: usize = 0;
#[hdl(cmp_eq)]
pub struct UnitOutRegNum<Width: Size> {
pub value: UIntType<Width>,
#[hdl(cmp_eq, no_static)]
pub struct UnitOutRegNum<C: Type + CpuConfigType> {
pub value: UIntType<UnitOutRegNumWidth<C>>,
pub config: C,
}
#[hdl(cmp_eq)]
#[hdl(cmp_eq, no_static)]
/// Physical Register Number -- registers in the CPU's backend
pub struct PRegNum<UnitNumWidth: Size, OutRegNumWidth: Size> {
pub unit_num: UnitNum<UnitNumWidth>,
pub unit_out_reg: UnitOutRegNum<OutRegNumWidth>,
pub struct PRegNum<C: Type + CpuConfigType> {
pub unit_num: UnitNum<C>,
pub unit_out_reg: UnitOutRegNum<C>,
}
impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWidth> {
impl<C: Type + CpuConfigType> PRegNum<C> {
#[hdl]
pub fn const_zero(self) -> Expr<Self> {
#[hdl]
@ -878,6 +882,7 @@ impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWi
unit_out_reg: #[hdl]
UnitOutRegNum {
value: 0u8.cast_to(self.unit_out_reg.value),
config: self.unit_out_reg.config,
},
}
}
@ -914,11 +919,9 @@ impl MOpRegNum {
//
// TODO: maybe add more registers later.
pub const FLAG_REG_NUMS: Range<u32> = 0xFE..0x100;
/// registers handled by a special small rename table (for flags and stuff, since it has more read/write ports)
pub const SPECIAL_REG_NUMS: Range<u32> = Self::FLAG_REG_NUMS;
/// registers handled by the large rename table for normal registers (has less read/write ports)
pub const NORMAL_REG_NUMS: Range<u32> =
Self::CONST_ZERO_REG_NUM + 1..Self::SPECIAL_REG_NUMS.start;
/// registers that aren't constants
pub const NON_CONST_REG_NUMS: Range<u32> =
Self::CONST_ZERO_REG_NUM + 1..Self::FLAG_REG_NUMS.end;
}
#[hdl(cmp_eq)]
@ -933,29 +936,6 @@ pub struct MOpDestReg {
pub flag_regs: Array<HdlOption<()>, { range_u32_len(&MOpRegNum::FLAG_REG_NUMS) }>,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum RenameTableName {
/// the large rename table for normal registers (has less read/write ports)
Normal,
/// a special small rename table (for flags and stuff, since it has more read/write ports)
Special,
}
impl RenameTableName {
pub const fn reg_range(self) -> std::ops::Range<u32> {
match self {
Self::Normal => MOpRegNum::NORMAL_REG_NUMS,
Self::Special => MOpRegNum::SPECIAL_REG_NUMS,
}
}
pub const fn as_str(self) -> &'static str {
match self {
Self::Normal => "rename_table_normal",
Self::Special => "rename_table_special",
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum MOpDestRegKind {
NormalReg {
@ -993,16 +973,13 @@ impl fmt::Display for MOpDestRegName {
}
impl MOpDestRegKind {
pub const fn reg_range(self) -> std::ops::Range<u32> {
pub const fn reg_num_range(self) -> std::ops::Range<u32> {
match self {
Self::NormalReg { .. } => MOpRegNum::NORMAL_REG_NUMS,
Self::FlagReg { .. } => MOpRegNum::FLAG_REG_NUMS,
}
}
pub const fn rename_table_names(self) -> &'static [RenameTableName] {
match self {
Self::NormalReg { .. } => &[RenameTableName::Normal, RenameTableName::Special],
Self::FlagReg { .. } => &[RenameTableName::Special],
Self::NormalReg { dest_reg_index: _ } => MOpRegNum::NON_CONST_REG_NUMS,
Self::FlagReg {
reg_num,
flag_reg_index: _,
} => reg_num..reg_num + 1,
}
}
pub fn fixed_reg_num(self) -> Option<u32> {
@ -1095,5 +1072,5 @@ pub type MOp = UnitMOp<
>;
#[hdl]
pub type RenamedMOp<DestReg: Type, SrcRegWidth: Size> =
UnitMOp<DestReg, SrcRegWidth, L2RegisterFileMOp<DestReg, SrcRegWidth>>;
pub type RenamedMOp<SrcRegWidth: Size> =
UnitMOp<(), SrcRegWidth, L2RegisterFileMOp<(), SrcRegWidth>>;

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

@ -2,7 +2,10 @@
// See Notices.txt for copyright information
pub mod config;
pub mod instruction;
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;

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,
Expr::ty(rob).renamed_insns_in.element().data.HdlNone(),
);
// TODO: finish
connect(
fetch_decode_interface.decoded_insns[fetch_index].ready,
@ -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(

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

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,19 +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, 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};
pub mod alu_branch;
pub mod unit_base;
@ -36,7 +38,7 @@ macro_rules! all_units {
}
) => {
$(#[$enum_meta])*
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
$vis enum $UnitKind {
$(
$(#[$variant_meta])*
@ -45,7 +47,7 @@ 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),)*
}
@ -204,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(Expr::ty(this)).$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(Expr::ty(this)).$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)
}
})*
};
};
}
@ -253,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
@ -282,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:
@ -312,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>;
@ -370,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)
}
@ -384,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)
}
@ -425,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)))
}
@ -439,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,29 +2,26 @@
// See Notices.txt for copyright information
use crate::{
config::CpuConfig,
config::{CpuConfig, PRegNumWidth},
instruction::{
AddSubMOp, AluBranchMOp, AluCommonMOp, COMMON_MOP_SRC_LEN, CommonMOp, LogicalMOp, MOpTrait,
OutputIntegerMode, RenamedMOp, UnitOutRegNum,
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 }>>,
@ -232,7 +229,7 @@ fn add_sub<SrcCount: KnownSize>(
#[hdl]
fn logical(
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize>>,
mop: Expr<LogicalMOp<(), DynSize>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
@ -245,20 +242,15 @@ fn logical(
}
#[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();
@ -279,13 +271,9 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
#[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(
@ -293,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,
)),
@ -311,10 +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,
insn.pc,
global_state.flags_mode,
src_values,
)),
@ -329,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,
@ -345,14 +336,14 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
#[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),
}
}
@ -361,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()
@ -379,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)
}
@ -393,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 = 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, 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)
@ -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,7 +342,7 @@ 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();
@ -309,7 +354,7 @@ pub fn unit_base<
Bool,
SourceLocation::caller(),
);
mem.depth(1 << config.out_reg_num_width);
mem.depth(1 << UnitOutRegNumWidth[config]);
mem
})
.collect();
@ -319,11 +364,11 @@ pub fn unit_base<
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,
},
),
@ -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,9 +548,9 @@ 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(
@ -517,12 +568,12 @@ pub fn unit_base<
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

@ -2,7 +2,6 @@
// See Notices.txt for copyright information
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);
@ -25,3 +24,16 @@ pub(crate) const fn range_u32_nth_or_panic(range: &std::ops::Range<u32>, index:
panic!("index out of range")
}
}
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,7 +159,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
#[hdl]
ArrayVec {
elements: self.elements.uninit(),
len: 0u8.cast_to(self.len),
len: self.len.zero(),
}
}
pub fn element(self) -> T {
@ -39,7 +179,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
let elements = elements.to_expr();
let len = len.to_expr();
assert_eq!(
Length[N::from_usize(Expr::ty(elements).len())],
Length::new(N::from_usize(Expr::ty(elements).len())),
Expr::ty(len),
"len type mismatch",
);
@ -54,7 +194,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
}
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()
@ -70,7 +210,7 @@ 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);
}
}
@ -112,6 +252,29 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
});
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>
@ -126,3 +289,35 @@ where
<ArrayType<T, N> as ExprIndex<Idx>>::expr_index(&this.elements, index)
}
}
#[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 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 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()
);
}
}
}

View file

@ -16832,6 +16832,38 @@ $upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
0vg
0wg
0xg
0yg
0zg
0{g
0|g
0}g
0~g
0!h
0"h
0#h
0$h
0%h
0&h
0'h
0(h
0)h
0*h
0+h
0,h
0-h
0.h
0/h
00h
01h
02h
03h
04h
05h
06h
07h
b0 (_
b0 ia
b0 )_
@ -17338,26 +17370,6 @@ b0 ga
b0 Jd
b0 ha
b0 Kd
b0 Ld
b0 Nd
b0 Md
b0 Od
0Pd
0Qd
0Rd
0Sd
0Td
0Ud
0Vd
0Wd
0Xd
0Yd
0Zd
0[d
0\d
0]d
0^d
0_d
0`d
0ad
0bd
@ -17374,326 +17386,6 @@ b0 Od
0md
0nd
0od
b0 pd
0"e
02e
0Be
0Re
0be
0re
0$f
04f
b0 qd
0#e
03e
0Ce
0Se
0ce
0se
0%f
05f
b0 rd
0$e
04e
0De
0Te
0de
0te
0&f
06f
b0 sd
0%e
05e
0Ee
0Ue
0ee
0ue
0'f
07f
b0 td
0&e
06e
0Fe
0Ve
0fe
0ve
0(f
08f
b0 ud
0'e
07e
0Ge
0We
0ge
0we
0)f
09f
b0 vd
0(e
08e
0He
0Xe
0he
0xe
0*f
0:f
b0 wd
0)e
09e
0Ie
0Ye
0ie
0ye
0+f
0;f
b0 xd
0*e
0:e
0Je
0Ze
0je
0ze
0,f
0<f
b0 yd
0+e
0;e
0Ke
0[e
0ke
0{e
0-f
0=f
b0 zd
0,e
0<e
0Le
0\e
0le
0|e
0.f
0>f
b0 {d
0-e
0=e
0Me
0]e
0me
0}e
0/f
0?f
b0 |d
0.e
0>e
0Ne
0^e
0ne
0~e
00f
0@f
b0 }d
0/e
0?e
0Oe
0_e
0oe
0!f
01f
0Af
b0 ~d
00e
0@e
0Pe
0`e
0pe
0"f
02f
0Bf
b0 !e
01e
0Ae
0Qe
0ae
0qe
0#f
03f
0Cf
b0 Df
0Tf
0df
0tf
0&g
06g
0Fg
0Vg
0fg
b0 Ef
0Uf
0ef
0uf
0'g
07g
0Gg
0Wg
0gg
b0 Ff
0Vf
0ff
0vf
0(g
08g
0Hg
0Xg
0hg
b0 Gf
0Wf
0gf
0wf
0)g
09g
0Ig
0Yg
0ig
b0 Hf
0Xf
0hf
0xf
0*g
0:g
0Jg
0Zg
0jg
b0 If
0Yf
0if
0yf
0+g
0;g
0Kg
0[g
0kg
b0 Jf
0Zf
0jf
0zf
0,g
0<g
0Lg
0\g
0lg
b0 Kf
0[f
0kf
0{f
0-g
0=g
0Mg
0]g
0mg
b0 Lf
0\f
0lf
0|f
0.g
0>g
0Ng
0^g
0ng
b0 Mf
0]f
0mf
0}f
0/g
0?g
0Og
0_g
0og
b0 Nf
0^f
0nf
0~f
00g
0@g
0Pg
0`g
0pg
b0 Of
0_f
0of
0!g
01g
0Ag
0Qg
0ag
0qg
b0 Pf
0`f
0pf
0"g
02g
0Bg
0Rg
0bg
0rg
b0 Qf
0af
0qf
0#g
03g
0Cg
0Sg
0cg
0sg
b0 Rf
0bf
0rf
0$g
04g
0Dg
0Tg
0dg
0tg
b0 Sf
0cf
0sf
0%g
05g
0Eg
0Ug
0eg
0ug
0vg
0wg
0xg
0yg
0zg
0{g
0|g
0}g
0~g
0!h
0"h
0#h
0$h
0%h
0&h
0'h
0(h
0)h
0*h
0+h
0,h
0-h
0.h
0/h
00h
01h
02h
03h
04h
05h
06h
07h
b0 8h
0Hh
0Xh
@ -17838,6 +17530,150 @@ b0 Gh
0Ii
0Yi
0ii
b0 Df
0Tf
0df
0tf
0&g
06g
0Fg
0Vg
0fg
b0 Ef
0Uf
0ef
0uf
0'g
07g
0Gg
0Wg
0gg
b0 Ff
0Vf
0ff
0vf
0(g
08g
0Hg
0Xg
0hg
b0 Gf
0Wf
0gf
0wf
0)g
09g
0Ig
0Yg
0ig
b0 Hf
0Xf
0hf
0xf
0*g
0:g
0Jg
0Zg
0jg
b0 If
0Yf
0if
0yf
0+g
0;g
0Kg
0[g
0kg
b0 Jf
0Zf
0jf
0zf
0,g
0<g
0Lg
0\g
0lg
b0 Kf
0[f
0kf
0{f
0-g
0=g
0Mg
0]g
0mg
b0 Lf
0\f
0lf
0|f
0.g
0>g
0Ng
0^g
0ng
b0 Mf
0]f
0mf
0}f
0/g
0?g
0Og
0_g
0og
b0 Nf
0^f
0nf
0~f
00g
0@g
0Pg
0`g
0pg
b0 Of
0_f
0of
0!g
01g
0Ag
0Qg
0ag
0qg
b0 Pf
0`f
0pf
0"g
02g
0Bg
0Rg
0bg
0rg
b0 Qf
0af
0qf
0#g
03g
0Cg
0Sg
0cg
0sg
b0 Rf
0bf
0rf
0$g
04g
0Dg
0Tg
0dg
0tg
b0 Sf
0cf
0sf
0%g
05g
0Eg
0Ug
0eg
0ug
b0 ji
0zi
0,j
@ -17982,6 +17818,170 @@ b0 yi
0{j
0-k
0=k
b0 pd
0"e
02e
0Be
0Re
0be
0re
0$f
04f
b0 qd
0#e
03e
0Ce
0Se
0ce
0se
0%f
05f
b0 rd
0$e
04e
0De
0Te
0de
0te
0&f
06f
b0 sd
0%e
05e
0Ee
0Ue
0ee
0ue
0'f
07f
b0 td
0&e
06e
0Fe
0Ve
0fe
0ve
0(f
08f
b0 ud
0'e
07e
0Ge
0We
0ge
0we
0)f
09f
b0 vd
0(e
08e
0He
0Xe
0he
0xe
0*f
0:f
b0 wd
0)e
09e
0Ie
0Ye
0ie
0ye
0+f
0;f
b0 xd
0*e
0:e
0Je
0Ze
0je
0ze
0,f
0<f
b0 yd
0+e
0;e
0Ke
0[e
0ke
0{e
0-f
0=f
b0 zd
0,e
0<e
0Le
0\e
0le
0|e
0.f
0>f
b0 {d
0-e
0=e
0Me
0]e
0me
0}e
0/f
0?f
b0 |d
0.e
0>e
0Ne
0^e
0ne
0~e
00f
0@f
b0 }d
0/e
0?e
0Oe
0_e
0oe
0!f
01f
0Af
b0 ~d
00e
0@e
0Pe
0`e
0pe
0"f
02f
0Bf
b0 !e
01e
0Ae
0Qe
0ae
0qe
0#f
03f
0Cf
0Pd
0Qd
0Rd
0Sd
0Td
0Ud
0Vd
0Wd
0Xd
0Yd
0Zd
0[d
0\d
0]d
0^d
0_d
b0 Ld
b0 Nd
b0 Md
b0 Od
0!
1"
sHdlSome\x20(1) #

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, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode},
reg_alloc::{FetchedDecodedMOp, reg_alloc},
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;

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