Compare commits

..

No commits in common. "next-pc" and "master" have entirely different histories.

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

167
Cargo.lock generated
View file

@ -1,6 +1,18 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 3
[[package]]
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]] [[package]]
name = "allocator-api2" name = "allocator-api2"
@ -81,12 +93,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.6.0" version = "2.6.0"
@ -166,15 +172,6 @@ dependencies = [
"strsim", "strsim",
] ]
[[package]]
name = "clap_complete"
version = "4.5.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2348487adcd4631696ced64ccdb40d38ac4d31cae7f2eec8817fcea1b9d1c43c"
dependencies = [
"clap",
]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.18" version = "4.5.18"
@ -210,7 +207,6 @@ name = "cpu"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"fayalite", "fayalite",
"serde",
] ]
[[package]] [[package]]
@ -304,22 +300,20 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]] [[package]]
name = "fayalite" name = "fayalite"
version = "0.3.0" version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#0b77d1bea0af932a40fd221daf65d5a9b7d62bc5" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e"
dependencies = [ dependencies = [
"base64",
"bitvec", "bitvec",
"blake3", "blake3",
"clap", "clap",
"clap_complete",
"ctor", "ctor",
"eyre", "eyre",
"fayalite-proc-macros", "fayalite-proc-macros",
"fayalite-visit-gen", "fayalite-visit-gen",
"hashbrown 0.15.5", "hashbrown",
"jobslot", "jobslot",
"num-bigint", "num-bigint",
"num-traits", "num-traits",
"ordered-float", "os_pipe",
"petgraph", "petgraph",
"serde", "serde",
"serde_json", "serde_json",
@ -331,7 +325,7 @@ dependencies = [
[[package]] [[package]]
name = "fayalite-proc-macros" name = "fayalite-proc-macros"
version = "0.3.0" version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#0b77d1bea0af932a40fd221daf65d5a9b7d62bc5" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e"
dependencies = [ dependencies = [
"fayalite-proc-macros-impl", "fayalite-proc-macros-impl",
] ]
@ -339,7 +333,7 @@ dependencies = [
[[package]] [[package]]
name = "fayalite-proc-macros-impl" name = "fayalite-proc-macros-impl"
version = "0.3.0" version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#0b77d1bea0af932a40fd221daf65d5a9b7d62bc5" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e"
dependencies = [ dependencies = [
"base16ct", "base16ct",
"num-bigint", "num-bigint",
@ -354,7 +348,7 @@ dependencies = [
[[package]] [[package]]
name = "fayalite-visit-gen" name = "fayalite-visit-gen"
version = "0.3.0" version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#0b77d1bea0af932a40fd221daf65d5a9b7d62bc5" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"prettyplease", "prettyplease",
@ -372,12 +366,6 @@ version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]] [[package]]
name = "funty" name = "funty"
version = "2.0.0" version = "2.0.0"
@ -396,14 +384,13 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.3.4" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "wasi",
"wasip2",
] ]
[[package]] [[package]]
@ -411,16 +398,9 @@ name = "hashbrown"
version = "0.14.5" version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [ dependencies = [
"ahash",
"allocator-api2", "allocator-api2",
"equivalent",
"foldhash",
] ]
[[package]] [[package]]
@ -451,7 +431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.14.5", "hashbrown",
"serde", "serde",
] ]
@ -469,16 +449,16 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "jobslot" name = "jobslot"
version = "0.2.23" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c" checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"derive_destructure2", "derive_destructure2",
"getrandom", "getrandom",
"libc", "libc",
"scopeguard", "scopeguard",
"windows-sys 0.61.2", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -534,26 +514,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]] [[package]]
name = "ordered-float" name = "os_pipe"
version = "5.1.0" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [ dependencies = [
"num-traits", "libc",
"rand", "windows-sys 0.59.0",
"serde",
] ]
[[package]] [[package]]
name = "petgraph" name = "petgraph"
version = "0.8.3" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"hashbrown 0.15.5",
"indexmap", "indexmap",
"serde",
] ]
[[package]] [[package]]
@ -584,37 +560,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]] [[package]]
name = "radium" name = "radium"
version = "0.7.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
"serde",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.37" version = "0.38.37"
@ -777,13 +728,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "wasip2" name = "wasi"
version = "1.0.1+wasi-0.2.4" version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
dependencies = [
"wit-bindgen",
]
[[package]] [[package]]
name = "which" name = "which"
@ -797,12 +745,6 @@ dependencies = [
"winsafe", "winsafe",
] ]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -821,15 +763,6 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.52.6"
@ -900,12 +833,6 @@ version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]] [[package]]
name = "wyz" name = "wyz"
version = "0.5.1" version = "0.5.1"
@ -914,3 +841,23 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [ dependencies = [
"tap", "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

@ -15,7 +15,6 @@ rust-version = "1.89.0"
[workspace.dependencies] [workspace.dependencies]
fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" } fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" }
serde = { version = "1.0.202", features = ["derive"] }
[profile.dev] [profile.dev]
opt-level = 1 opt-level = 1

View file

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

View file

@ -16,4 +16,3 @@ version.workspace = true
[dependencies] [dependencies]
fayalite.workspace = true fayalite.workspace = true
serde.workspace = true

View file

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

View file

@ -8,10 +8,9 @@ use crate::{
}, },
}; };
use fayalite::prelude::*; use fayalite::prelude::*;
use serde::{Deserialize, Serialize};
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct UnitConfig { pub struct UnitConfig {
pub kind: UnitKind, pub kind: UnitKind,
@ -28,14 +27,12 @@ impl UnitConfig {
} }
} }
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub struct CpuConfig { pub struct CpuConfig {
pub units: Vec<UnitConfig>, pub units: Vec<UnitConfig>,
pub out_reg_num_width: usize, pub out_reg_num_width: usize,
pub fetch_width: NonZeroUsize, pub fetch_width: NonZeroUsize,
pub max_branches_per_fetch: NonZeroUsize,
pub fetch_width_in_bytes: NonZeroUsize,
/// default value for [`UnitConfig::max_in_flight`] /// default value for [`UnitConfig::max_in_flight`]
pub default_unit_max_in_flight: NonZeroUsize, pub default_unit_max_in_flight: NonZeroUsize,
pub rob_size: NonZeroUsize, pub rob_size: NonZeroUsize,
@ -49,18 +46,6 @@ impl CpuConfig {
}; };
v v
}; };
pub const DEFAULT_MAX_BRANCHES_PER_FETCH: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(1) else {
unreachable!();
};
v
};
pub const DEFAULT_FETCH_WIDTH_IN_BYTES: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(4) else {
unreachable!();
};
v
};
pub const DEFAULT_UNIT_MAX_IN_FLIGHT: NonZeroUsize = { pub const DEFAULT_UNIT_MAX_IN_FLIGHT: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(8) else { let Some(v) = NonZeroUsize::new(8) else {
unreachable!(); unreachable!();
@ -72,8 +57,6 @@ impl CpuConfig {
units, units,
out_reg_num_width: Self::DEFAULT_OUT_REG_NUM_WIDTH, out_reg_num_width: Self::DEFAULT_OUT_REG_NUM_WIDTH,
fetch_width: Self::DEFAULT_FETCH_WIDTH, fetch_width: Self::DEFAULT_FETCH_WIDTH,
max_branches_per_fetch: Self::DEFAULT_MAX_BRANCHES_PER_FETCH,
fetch_width_in_bytes: Self::DEFAULT_FETCH_WIDTH_IN_BYTES,
default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT, default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT,
rob_size, rob_size,
} }
@ -134,12 +117,3 @@ impl CpuConfig {
[self.non_const_unit_nums().len()] [self.non_const_unit_nums().len()]
} }
} }
#[hdl(get(|c| c.fetch_width.get()))]
pub type CpuConfigFetchWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.max_branches_per_fetch.get()))]
pub type CpuConfigMaxBranchesPerFetch<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.fetch_width_in_bytes.get()))]
pub type CpuConfigFetchWidthInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;

View file

@ -5,7 +5,6 @@ use fayalite::{
expr::ops::{ArrayLiteral, ExprPartialEq}, expr::ops::{ArrayLiteral, ExprPartialEq},
intern::Interned, intern::Interned,
prelude::*, prelude::*,
sim::value::SimValuePartialEq,
}; };
use std::{fmt, marker::PhantomData, ops::Range}; use std::{fmt, marker::PhantomData, ops::Range};
@ -182,12 +181,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_IMM_WIDTH: usize = 34;
pub const MOP_MIN_REG_WIDTH: usize = 8; pub const MOP_MIN_REG_WIDTH: usize = 8;
pub const COMMON_MOP_SRC_LEN: usize = 3; pub const COMMON_MOP_SRC_LEN: usize = 3;

View file

@ -2,7 +2,6 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
pub mod config; pub mod config;
pub mod instruction; pub mod instruction;
pub mod next_pc;
pub mod reg_alloc; pub mod reg_alloc;
pub mod register; pub mod register;
pub mod unit; pub mod unit;

View file

@ -1,561 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! [Next-Instruction Logic](https://git.libre-chip.org/libre-chip/grant-tracking/issues/10)
//!
//! The basic idea here is that there's a `next_pc` stage that sends predicted fetch PCs to the `fetch` stage,
//! the `fetch` stage's outputs eventually end up in the `decode` stage,
//! after the `decode` stage there's a `post_decode` stage (that may run in the same clock cycle as `decode`)
//! that checks that the fetched instructions' kinds match the predicted instruction kinds and that feeds
//! information back to the `fetch` stage to cancel fetches that need to be predicted differently.
use crate::{config::CpuConfig, util::array_vec::ArrayVec};
use fayalite::{
int::{UIntInRange, UIntInRangeInclusive},
prelude::*,
sim::value::SimOnlyValueTrait,
util::ready_valid::ReadyValid,
};
#[hdl]
pub enum PredictedCond {
Taken,
Fallthrough,
}
#[hdl]
pub struct PredictedFallthrough {}
#[hdl]
pub enum BranchPredictionKind<CondKind> {
Branch(HdlOption<CondKind>),
IndirectBranch(HdlOption<CondKind>),
Call(HdlOption<CondKind>),
IndirectCall(HdlOption<CondKind>),
Ret(HdlOption<CondKind>),
}
#[hdl(get(|c| c.max_branches_per_fetch.get() - 1))]
pub type NextPcPredictionMaxBranchesBeforeLast<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(no_static)]
pub struct NextPcPrediction<C: PhantomConstGet<CpuConfig>> {
pub fetch_pc: UInt<64>,
pub async_interrupt: Bool,
pub branches_before_last: ArrayVec<
BranchPredictionKind<PredictedFallthrough>,
NextPcPredictionMaxBranchesBeforeLast<C>,
>,
pub last_branch: HdlOption<BranchPredictionKind<PredictedCond>>,
pub last_branch_target_pc: UInt<64>,
}
#[hdl]
pub struct NextPcToFetchInterfaceInner {
pub next_fetch_pc: UInt<64>,
pub fetch_block_id: UInt<8>,
pub in_progress_fetches_to_cancel: UInt<8>,
}
#[hdl(no_static)]
pub struct NextPcToFetchInterface<C: PhantomConstGet<CpuConfig>> {
pub inner: ReadyValid<NextPcToFetchInterfaceInner>,
pub config: C,
}
#[hdl]
/// WIP version of decoded instruction just good enough to represent stuff needed for [`next_pc()`] since the actual instruction definition isn't finalized yet. This will be replaced at a later point.
pub enum WipDecodedInsnKind {
NonBranch,
Branch(UInt<64>),
BranchCond(UInt<64>),
IndirectBranch,
IndirectBranchCond,
Call(UInt<64>),
CallCond(UInt<64>),
IndirectCall,
IndirectCallCond,
Ret,
RetCond,
/// not actually an instruction read from memory, covers stuff like external interrupts, page faults, memory errors, and so on.
Interrupt(UInt<64>),
}
#[hdl]
/// WIP version of decoded instruction just good enough to represent stuff needed for [`next_pc()`] since the actual instruction definition isn't finalized yet. This will be replaced at a later point.
pub struct WipDecodedInsn {
pub fetch_block_id: UInt<8>,
pub id: UInt<12>,
pub pc: UInt<64>,
pub kind: WipDecodedInsnKind,
}
#[hdl(no_static)]
/// handles updating speculative branch predictor state (e.g. branch histories) when instructions retire,
/// as well as updating state when a branch instruction is mis-speculated.
pub struct NextPcToRetireInterface<C: PhantomConstGet<CpuConfig>> {
// TODO: add needed fields
pub config: C,
}
#[hdl(no_static)]
pub struct DecodeToPostDecodeInterface<C: PhantomConstGet<CpuConfig>> {
// TODO: add needed fields
pub config: C,
}
#[hdl(no_static)]
pub struct PostDecodeOutputInterface<C: PhantomConstGet<CpuConfig>> {
// TODO: add needed fields
pub config: C,
}
#[derive(
Copy, Clone, PartialEq, Eq, Debug, Hash, Default, serde::Serialize, serde::Deserialize,
)]
enum BranchPredictionState {
StronglyNotTaken,
#[default]
WeaklyNotTaken,
WeaklyTaken,
StronglyTaken,
}
impl BranchPredictionState {
#[must_use]
fn is_taken(self) -> bool {
match self {
Self::StronglyNotTaken => false,
Self::WeaklyNotTaken => false,
Self::WeaklyTaken => true,
Self::StronglyTaken => true,
}
}
#[must_use]
fn towards_taken(self) -> Self {
match self {
Self::StronglyNotTaken => Self::WeaklyNotTaken,
Self::WeaklyNotTaken => Self::WeaklyTaken,
Self::WeaklyTaken => Self::StronglyTaken,
Self::StronglyTaken => Self::StronglyTaken,
}
}
#[must_use]
fn towards_not_taken(self) -> Self {
match self {
Self::StronglyNotTaken => Self::StronglyNotTaken,
Self::WeaklyNotTaken => Self::StronglyNotTaken,
Self::WeaklyTaken => Self::WeaklyNotTaken,
Self::StronglyTaken => Self::WeaklyTaken,
}
}
}
#[derive(Copy, Clone, Debug)]
#[must_use]
enum ResetStatus {
Done,
Working,
}
impl ResetStatus {
fn and(self, other: Self) -> Self {
match (self, other) {
(ResetStatus::Done, ResetStatus::Done) => ResetStatus::Done,
(ResetStatus::Done | ResetStatus::Working, ResetStatus::Working)
| (ResetStatus::Working, ResetStatus::Done) => ResetStatus::Working,
}
}
}
trait SimValueDefault: Type {
fn sim_value_default(self) -> SimValue<Self>;
}
impl<T: SimOnlyValueTrait> SimValueDefault for SimOnly<T> {
fn sim_value_default(self) -> SimValue<Self> {
SimOnlyValue::<T>::default().to_sim_value_with_type(self)
}
}
impl<T: Type> SimValueDefault for HdlOption<T> {
fn sim_value_default(self) -> SimValue<Self> {
self.HdlNone().to_sim_value_with_type(self)
}
}
impl SimValueDefault for Bool {
fn sim_value_default(self) -> SimValue<Self> {
false.to_sim_value()
}
}
impl<Width: Size> SimValueDefault for UIntType<Width> {
fn sim_value_default(self) -> SimValue<Self> {
self.zero().to_sim_value()
}
}
trait ResetSteps: Type {
async fn reset_step(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
step: usize,
) -> ResetStatus;
}
impl<T: SimValueDefault, Len: Size> ResetSteps for ArrayType<T, Len> {
async fn reset_step(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
step: usize,
) -> ResetStatus {
let element = Expr::ty(this).element();
let len = Expr::ty(this).len();
if step < len {
sim.write(this[step], element.sim_value_default()).await;
}
if step.saturating_add(1) >= len {
ResetStatus::Done
} else {
ResetStatus::Working
}
}
}
#[hdl]
struct CallStack {
return_addresses: Array<UInt<64>, { CallStack::SIZE }>,
len: UIntInRangeInclusive<0, { CallStack::SIZE }>,
}
impl CallStack {
const SIZE: usize = 16;
}
impl SimValueDefault for CallStack {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
#[hdl(sim)]
CallStack {
// something other than zero so you can see the values getting reset
return_addresses: [!0u64; Self::SIZE],
len: 0usize.to_sim_value_with_type(self.len),
}
}
}
impl ResetSteps for CallStack {
#[hdl]
async fn reset_step(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
_step: usize,
) -> ResetStatus {
#[hdl]
let CallStack {
return_addresses,
len,
} = this;
// return_addresses is implemented as a shift register, so it can be all reset at once
for i in return_addresses {
sim.write(i, 0u64).await;
}
sim.write(len, 0usize).await;
ResetStatus::Done
}
}
#[hdl]
struct BranchTargetBuffer {
branch_pc_to_target_map: Array<HdlOption<(UInt<64>, UInt<64>)>, { BranchTargetBuffer::SIZE }>,
}
impl BranchTargetBuffer {
const SIZE: usize = 16;
}
impl SimValueDefault for BranchTargetBuffer {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
#[hdl(sim)]
BranchTargetBuffer {
// something other than zero so you can see the values getting reset
branch_pc_to_target_map: [HdlSome((0u64, 0u64)); Self::SIZE],
}
}
}
impl ResetSteps for BranchTargetBuffer {
#[hdl]
async fn reset_step(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
step: usize,
) -> ResetStatus {
#[hdl]
let BranchTargetBuffer {
branch_pc_to_target_map,
} = this;
ResetSteps::reset_step(branch_pc_to_target_map, sim, step).await
}
}
#[hdl]
struct BranchHistory {
history: Array<Bool, { BranchHistory::SIZE }>,
/// exclusive
tail: UIntInRange<0, { BranchHistory::SIZE }>,
/// inclusive, always at or after tail, always at or before speculative_head
non_speculative_head: UIntInRange<0, { BranchHistory::SIZE }>,
/// inclusive, always at or after both tail and non_speculative_head
speculative_head: UIntInRange<0, { BranchHistory::SIZE }>,
}
impl ResetSteps for BranchHistory {
#[hdl]
async fn reset_step(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
step: usize,
) -> ResetStatus {
#[hdl]
let Self {
history,
tail,
non_speculative_head,
speculative_head,
} = this;
sim.write(tail, 0usize).await;
sim.write(non_speculative_head, 0usize).await;
sim.write(speculative_head, 0usize).await;
ResetSteps::reset_step(history, sim, step).await
}
}
impl SimValueDefault for BranchHistory {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
#[hdl(sim)]
BranchHistory {
// something other than zero so you can see the values getting reset
history: [true; Self::SIZE],
tail: 0usize.to_sim_value_with_type(self.tail),
non_speculative_head: 0usize.to_sim_value_with_type(self.non_speculative_head),
speculative_head: 0usize.to_sim_value_with_type(self.speculative_head),
}
}
}
enum BranchHistoryTryPushSpeculativeError {
NoSpace,
}
enum BranchHistoryTryPushNonSpeculativeError {
NoSpace,
Misprediction { speculated: bool },
}
impl BranchHistory {
const LOG2_SIZE: usize = 8;
const SIZE: usize = 1 << Self::LOG2_SIZE;
fn next_pos(pos: usize) -> usize {
(pos + 1) % Self::SIZE
}
fn prev_pos(pos: usize) -> usize {
(pos + Self::SIZE - 1) % Self::SIZE
}
async fn history_from_head<const N: usize>(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
head: usize,
) -> [bool; N] {
let mut retval = [false; N];
let mut pos = head;
for entry in &mut retval {
if pos == *sim.read(this.tail).await {
break;
}
*entry = sim.read_bool(this.history[pos]).await;
pos = Self::prev_pos(pos);
}
retval
}
async fn delete_speculative_history(this: Expr<Self>, sim: &mut ExternModuleSimulationState) {
let non_speculative_head = sim.read(this.non_speculative_head).await;
sim.write(this.speculative_head, non_speculative_head).await;
}
async fn recent_history_including_speculative<const N: usize>(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
) -> [bool; N] {
let head = *sim.read(this.speculative_head).await;
Self::history_from_head(this, sim, head).await
}
async fn speculative_full(this: Expr<Self>, sim: &mut ExternModuleSimulationState) -> bool {
let speculative_head = *sim.read(this.speculative_head).await;
Self::next_pos(speculative_head) == *sim.read(this.tail).await
}
async fn try_push_speculative(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
value: bool,
) -> Result<(), BranchHistoryTryPushSpeculativeError> {
if Self::speculative_full(this, sim).await {
Err(BranchHistoryTryPushSpeculativeError::NoSpace)
} else {
let speculative_head = *sim.read(this.speculative_head).await;
let speculative_head = Self::next_pos(speculative_head);
sim.write(this.speculative_head, speculative_head).await;
sim.write(this.history[speculative_head], value).await;
Ok(())
}
}
async fn try_push_non_speculative(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
value: bool,
) -> Result<(), BranchHistoryTryPushNonSpeculativeError> {
let speculative_head = *sim.read(this.speculative_head).await;
let non_speculative_head = *sim.read(this.non_speculative_head).await;
if speculative_head == non_speculative_head {
Err(BranchHistoryTryPushNonSpeculativeError::NoSpace)
} else {
let pos = Self::next_pos(non_speculative_head);
let speculated = sim.read_bool(this.history[pos]).await;
if speculated != value {
Err(BranchHistoryTryPushNonSpeculativeError::Misprediction { speculated })
} else {
sim.write(this.non_speculative_head, pos).await;
Ok(())
}
}
}
}
#[hdl]
pub struct NextPcState {
speculative_call_stack: CallStack,
non_speculative_call_stack: CallStack,
branch_target_buffer: BranchTargetBuffer,
branch_history: BranchHistory,
branch_predictor: Array<SimOnly<BranchPredictionState>, { NextPcState::BRANCH_PREDICTOR_SIZE }>,
}
impl NextPcState {
const BRANCH_PREDICTOR_LOG2_SIZE: usize = 8;
const BRANCH_PREDICTOR_SIZE: usize = 1 << Self::BRANCH_PREDICTOR_LOG2_SIZE;
async fn branch_predictor_index(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
pc: u64,
) -> usize {
let mut history = 0u64;
let history_bits: [bool; Self::BRANCH_PREDICTOR_LOG2_SIZE] =
BranchHistory::recent_history_including_speculative(this.branch_history, sim).await;
for history_bit in history_bits {
history <<= 1;
if history_bit {
history |= 1;
}
}
let mut t = history;
t ^= t.rotate_left(5) & !pc.rotate_right(3);
t ^= pc;
t ^= !t.rotate_left(2) & t.rotate_left(4);
let mut retval = 0;
for i in (0..Self::BRANCH_PREDICTOR_LOG2_SIZE).step_by(Self::BRANCH_PREDICTOR_LOG2_SIZE) {
retval ^= t >> i;
}
retval as usize % Self::BRANCH_PREDICTOR_SIZE
}
}
impl SimValueDefault for NextPcState {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
let Self {
speculative_call_stack,
non_speculative_call_stack,
branch_target_buffer,
branch_history,
branch_predictor,
} = self;
#[hdl(sim)]
Self {
speculative_call_stack: speculative_call_stack.sim_value_default(),
non_speculative_call_stack: non_speculative_call_stack.sim_value_default(),
branch_target_buffer: branch_target_buffer.sim_value_default(),
branch_history: branch_history.sim_value_default(),
// use something other than the default so you can see the reset progress
branch_predictor: std::array::from_fn(|_| {
SimOnlyValue::new(BranchPredictionState::default().towards_not_taken())
}),
}
}
}
impl ResetSteps for NextPcState {
#[hdl]
async fn reset_step(
this: Expr<Self>,
sim: &mut ExternModuleSimulationState,
step: usize,
) -> ResetStatus {
#[hdl]
let NextPcState {
speculative_call_stack,
non_speculative_call_stack,
branch_target_buffer,
branch_history,
branch_predictor,
} = this;
let speculative_call_stack =
ResetSteps::reset_step(speculative_call_stack, sim, step).await;
let non_speculative_call_stack =
ResetSteps::reset_step(non_speculative_call_stack, sim, step).await;
let branch_target_buffer = ResetSteps::reset_step(branch_target_buffer, sim, step).await;
let branch_history = ResetSteps::reset_step(branch_history, sim, step).await;
let branch_predictor = ResetSteps::reset_step(branch_predictor, sim, step).await;
speculative_call_stack
.and(non_speculative_call_stack)
.and(branch_target_buffer)
.and(branch_history)
.and(branch_predictor)
}
}
#[hdl_module(extern)]
pub fn next_pc(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let to_fetch: NextPcToFetchInterface<PhantomConst<CpuConfig>> =
m.output(NextPcToFetchInterface[config]);
#[hdl]
let state_for_debug: NextPcState = m.output();
m.extern_module_simulation_fn(
(cd, to_fetch, state_for_debug),
|(cd, to_fetch, state_for_debug), mut sim| async move {
sim.write(state_for_debug, NextPcState.sim_value_default())
.await;
sim.resettable(
cd,
|mut sim: ExternModuleSimulationState| async move {
sim.write(to_fetch.inner.data, HdlNone()).await;
},
|mut sim: ExternModuleSimulationState, ()| async move {
for step in 0usize.. {
sim.wait_for_clock_edge(cd.clk).await;
match ResetSteps::reset_step(state_for_debug, &mut sim, step).await {
ResetStatus::Done => break,
ResetStatus::Working => {}
}
}
// TODO: finish
},
)
.await;
},
);
}

View file

@ -120,7 +120,10 @@ pub fn unit_free_regs_tracker(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use fayalite::{firrtl::ExportOptions, module::transform::simplify_enums::SimplifyEnumsKind}; use fayalite::{
cli::FormalMode, firrtl::ExportOptions,
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
};
use std::num::NonZero; use std::num::NonZero;
fn test_unit_free_regs_tracker( fn test_unit_free_regs_tracker(

View file

@ -15,7 +15,6 @@ use fayalite::{
intern::{Intern, Interned}, intern::{Intern, Interned},
prelude::*, prelude::*,
}; };
use serde::{Deserialize, Serialize};
pub mod alu_branch; pub mod alu_branch;
pub mod unit_base; pub mod unit_base;
@ -37,7 +36,7 @@ macro_rules! all_units {
} }
) => { ) => {
$(#[$enum_meta])* $(#[$enum_meta])*
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
$vis enum $UnitKind { $vis enum $UnitKind {
$( $(
$(#[$variant_meta])* $(#[$variant_meta])*

View file

@ -1,10 +1,147 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use fayalite::{expr::ops::ExprIndex, int::UIntInRangeInclusiveType, prelude::*}; use fayalite::{
expr::ops::{ExprCastTo, ExprIndex, ExprPartialEq, ExprPartialOrd},
int::SizeType,
intern::{Intern, Interned},
prelude::*,
ty::{MatchVariantWithoutScope, StaticType, TypeProperties},
};
use std::{marker::PhantomData, ops::Index};
#[hdl] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub type Length<Max: Size> = UIntInRangeInclusiveType<ConstUsize<0>, Max>; 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 /// like [`std::vec::Vec`], except with a [`Expr`] for [`len()`][`Self::len()`] and a fixed capacity
#[hdl] #[hdl]
@ -19,7 +156,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
#[hdl] #[hdl]
ArrayVec { ArrayVec {
elements: self.elements.uninit(), elements: self.elements.uninit(),
len: 0u8.cast_to(self.len), len: self.len.zero(),
} }
} }
pub fn element(self) -> T { pub fn element(self) -> T {
@ -39,7 +176,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
let elements = elements.to_expr(); let elements = elements.to_expr();
let len = len.to_expr(); let len = len.to_expr();
assert_eq!( assert_eq!(
Length[N::from_usize(Expr::ty(elements).len())], Length::new(N::from_usize(Expr::ty(elements).len())),
Expr::ty(len), Expr::ty(len),
"len type mismatch", "len type mismatch",
); );
@ -54,7 +191,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
} }
pub fn is_empty(this: impl ToExpr<Type = Self>) -> Expr<Bool> { pub fn is_empty(this: impl ToExpr<Type = Self>) -> Expr<Bool> {
let len = Self::len(this); let len = Self::len(this);
len.cmp_eq(0u8) len.cmp_eq(Expr::ty(len).zero())
} }
pub fn capacity(self) -> usize { pub fn capacity(self) -> usize {
self.elements.len() self.elements.len()
@ -70,7 +207,7 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
let this = this.to_expr(); let this = this.to_expr();
for (index, element) in this.elements.into_iter().enumerate() { for (index, element) in this.elements.into_iter().enumerate() {
#[hdl] #[hdl]
if index.cmp_lt(this.len) { if index.cmp_lt(Length::as_uint(this.len)) {
f(index, element); f(index, element);
} }
} }

View file

@ -16832,6 +16832,38 @@ $upscope $end
$upscope $end $upscope $end
$enddefinitions $end $enddefinitions $end
$dumpvars $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 (_
b0 ia b0 ia
b0 )_ b0 )_
@ -17338,26 +17370,6 @@ b0 ga
b0 Jd b0 Jd
b0 ha b0 ha
b0 Kd 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 0`d
0ad 0ad
0bd 0bd
@ -17374,326 +17386,6 @@ b0 Od
0md 0md
0nd 0nd
0od 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 b0 8h
0Hh 0Hh
0Xh 0Xh
@ -17838,6 +17530,150 @@ b0 Gh
0Ii 0Ii
0Yi 0Yi
0ii 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 b0 ji
0zi 0zi
0,j 0,j
@ -17982,6 +17818,170 @@ b0 yi
0{j 0{j
0-k 0-k
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! 0!
1" 1"
sHdlSome\x20(1) # sHdlSome\x20(1) #

View file

@ -1,45 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use cpu::{
config::{CpuConfig, UnitConfig},
next_pc::next_pc,
unit::UnitKind,
};
use fayalite::{prelude::*, sim::vcd::VcdWriterDecls, util::RcWriter};
use std::num::NonZeroUsize;
#[hdl]
#[test]
fn test_next_pc() {
let _n = SourceLocation::normalize_files_for_tests();
let mut config = CpuConfig::new(
vec![
UnitConfig::new(UnitKind::AluBranch),
UnitConfig::new(UnitKind::AluBranch),
],
NonZeroUsize::new(20).unwrap(),
);
config.fetch_width = NonZeroUsize::new(2).unwrap();
let m = next_pc(PhantomConst::new_sized(config));
let mut sim = Simulation::new(m);
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
let to_fetch = sim.io().to_fetch;
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
sim.write_bool(to_fetch.inner.ready, true);
for _cycle in 0..300 {
sim.advance_time(SimDuration::from_nanos(500));
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_nanos(500));
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, false);
}
// FIXME: vcd is just whatever next_pc does now, which isn't known to be correct
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("expected/next_pc.vcd") {
panic!();
}
}

View file

@ -9,6 +9,8 @@ use cpu::{
unit::{GlobalState, UnitKind}, unit::{GlobalState, UnitKind},
}; };
use fayalite::{ use fayalite::{
assert_export_firrtl,
firrtl::ExportOptions,
prelude::*, prelude::*,
sim::{Simulation, time::SimDuration, vcd::VcdWriterDecls}, sim::{Simulation, time::SimDuration, vcd::VcdWriterDecls},
util::RcWriter, util::RcWriter,