This commit is contained in:
parent
d0aa86e335
commit
cb5855589f
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -279,7 +279,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fayalite"
|
name = "fayalite"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d0229fbcfb11666b5abf7ae487ffe335866f9326"
|
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec",
|
"bitvec",
|
||||||
"blake3",
|
"blake3",
|
||||||
|
@ -300,7 +300,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fayalite-proc-macros"
|
name = "fayalite-proc-macros"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d0229fbcfb11666b5abf7ae487ffe335866f9326"
|
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fayalite-proc-macros-impl",
|
"fayalite-proc-macros-impl",
|
||||||
]
|
]
|
||||||
|
@ -308,7 +308,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fayalite-proc-macros-impl"
|
name = "fayalite-proc-macros-impl"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d0229fbcfb11666b5abf7ae487ffe335866f9326"
|
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base16ct",
|
"base16ct",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
|
@ -323,7 +323,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fayalite-visit-gen"
|
name = "fayalite-visit-gen"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d0229fbcfb11666b5abf7ae487ffe335866f9326"
|
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
// 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 crate::instruction::{PRegNum, UnitKind, UnitNum};
|
use crate::{
|
||||||
|
instruction::{PRegNum, UnitNum, CONST_ZERO_UNIT_NUM},
|
||||||
|
unit::UnitKind,
|
||||||
|
};
|
||||||
use fayalite::prelude::*;
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct CpuConfig {
|
pub struct CpuConfig {
|
||||||
pub units: Vec<UnitKind>,
|
pub unit_kinds: Vec<UnitKind>,
|
||||||
pub out_reg_num_width: usize,
|
pub out_reg_num_width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CpuConfig {
|
impl CpuConfig {
|
||||||
pub fn unit_num_width(&self) -> usize {
|
pub fn unit_num_width(&self) -> usize {
|
||||||
UInt::range(0..self.units.len()).width()
|
UInt::range((CONST_ZERO_UNIT_NUM + 1)..self.unit_kinds.len()).width()
|
||||||
}
|
}
|
||||||
pub fn unit_num(&self) -> UnitNum<DynSize> {
|
pub fn unit_num(&self) -> UnitNum<DynSize> {
|
||||||
UnitNum[self.unit_num_width()]
|
UnitNum[self.unit_num_width()]
|
||||||
|
|
|
@ -1,102 +1,11 @@
|
||||||
// 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 crate::config::CpuConfig;
|
use crate::unit::UnitMOp;
|
||||||
use fayalite::prelude::*;
|
use fayalite::prelude::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub mod power_isa;
|
pub mod power_isa;
|
||||||
|
|
||||||
macro_rules! all_units {
|
|
||||||
(
|
|
||||||
#[hdl_unit_kind = $HdlUnitKind:ident]
|
|
||||||
#[unit_kind = $UnitKind:ident]
|
|
||||||
#[hdl]
|
|
||||||
$(#[$enum_meta:meta])*
|
|
||||||
$vis:vis enum $UnitMOpEnum:ident<$RegWidth:ident: Size> {
|
|
||||||
$(
|
|
||||||
$(#[$variant_meta:meta])*
|
|
||||||
$Unit:ident($Op:ty),
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
$(#[$enum_meta])*
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
|
||||||
$vis enum $UnitKind {
|
|
||||||
$(
|
|
||||||
$(#[$variant_meta])*
|
|
||||||
$Unit,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToExpr for $UnitKind {
|
|
||||||
type Type = $HdlUnitKind;
|
|
||||||
|
|
||||||
fn to_expr(&self) -> Expr<Self::Type> {
|
|
||||||
match self {
|
|
||||||
$($UnitKind::$Unit => $HdlUnitKind.$Unit(),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
$(#[$enum_meta])*
|
|
||||||
$vis enum $HdlUnitKind {
|
|
||||||
$(
|
|
||||||
$(#[$variant_meta])*
|
|
||||||
$Unit,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
|
||||||
$(#[$enum_meta])*
|
|
||||||
$vis enum $UnitMOpEnum<$RegWidth: Size> {
|
|
||||||
$(
|
|
||||||
$(#[$variant_meta])*
|
|
||||||
$Unit($Op),
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$RegWidth: Size> $UnitMOpEnum<$RegWidth> {
|
|
||||||
#[hdl]
|
|
||||||
$vis fn kind(expr: impl ToExpr<Type = Self>) -> Expr<$HdlUnitKind> {
|
|
||||||
#[hdl]
|
|
||||||
let unit_kind = wire();
|
|
||||||
#[hdl]
|
|
||||||
match expr {
|
|
||||||
$($UnitMOpEnum::<$RegWidth>::$Unit(_) => connect(unit_kind, $HdlUnitKind.$Unit()),)*
|
|
||||||
}
|
|
||||||
unit_kind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CpuConfig {
|
|
||||||
#[hdl]
|
|
||||||
pub fn available_units_for_kind(&self, unit_kind: impl ToExpr<Type = $HdlUnitKind>) -> Expr<Array<Bool>> {
|
|
||||||
#[hdl]
|
|
||||||
let available_units_for_kind = wire(Array[Bool][self.units.len()]);
|
|
||||||
#[hdl]
|
|
||||||
match unit_kind {
|
|
||||||
$($HdlUnitKind::$Unit => for (index, &unit) in self.units.iter().enumerate() {
|
|
||||||
connect(available_units_for_kind[index], unit == $UnitKind::$Unit);
|
|
||||||
})*
|
|
||||||
}
|
|
||||||
available_units_for_kind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
all_units! {
|
|
||||||
#[hdl_unit_kind = HdlUnitKind]
|
|
||||||
#[unit_kind = UnitKind]
|
|
||||||
#[hdl]
|
|
||||||
pub enum UnitMOp<RegWidth: Size> {
|
|
||||||
AluBranch(AluBranchMOp<RegWidth>),
|
|
||||||
L2RegisterFile(L2RegisterFileMOp<RegWidth>),
|
|
||||||
LoadStore(LoadStoreMOp<RegWidth>),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub enum OutputIntegerMode {
|
pub enum OutputIntegerMode {
|
||||||
Full64,
|
Full64,
|
||||||
|
@ -239,11 +148,14 @@ pub enum LoadStoreMOp<RegWidth: Size> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
/// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind
|
/// 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 struct UnitNum<Width: Size> {
|
||||||
pub value: UIntType<Width>,
|
pub value: UIntType<Width>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const CONST_ZERO_UNIT_NUM: usize = 0;
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub struct UnitOutRegNum<Width: Size> {
|
pub struct UnitOutRegNum<Width: Size> {
|
||||||
pub value: UIntType<Width>,
|
pub value: UIntType<Width>,
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
// 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 reg_alloc;
|
||||||
pub mod register;
|
pub mod register;
|
||||||
|
pub mod unit;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
11
crates/cpu/src/reg_alloc.rs
Normal file
11
crates/cpu/src/reg_alloc.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
use crate::config::CpuConfig;
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
pub mod unit_free_regs_tracker;
|
||||||
|
|
||||||
|
#[hdl_module]
|
||||||
|
pub fn reg_alloc(config: CpuConfig) {
|
||||||
|
todo!()
|
||||||
|
}
|
110
crates/cpu/src/reg_alloc/unit_free_regs_tracker.rs
Normal file
110
crates/cpu/src/reg_alloc/unit_free_regs_tracker.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
use crate::util::tree_reduce::tree_reduce;
|
||||||
|
use fayalite::{prelude::*, util::ready_valid::ReadyValid};
|
||||||
|
use std::{num::NonZeroUsize, ops::Range};
|
||||||
|
|
||||||
|
#[hdl_module]
|
||||||
|
pub fn unit_free_regs_tracker(
|
||||||
|
free_at_once: NonZeroUsize,
|
||||||
|
alloc_at_once: NonZeroUsize,
|
||||||
|
reg_num_width: usize,
|
||||||
|
) {
|
||||||
|
#[hdl]
|
||||||
|
let cd: ClockDomain = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let free_in: Array<ReadyValid<UInt>> =
|
||||||
|
m.input(Array[ReadyValid[UInt[reg_num_width]]][free_at_once.get()]);
|
||||||
|
#[hdl]
|
||||||
|
let alloc_out: Array<ReadyValid<UInt>> =
|
||||||
|
m.input(Array[ReadyValid[UInt[reg_num_width]]][alloc_at_once.get()]);
|
||||||
|
assert!(
|
||||||
|
reg_num_width <= isize::MAX.ilog2() as usize,
|
||||||
|
"too many registers"
|
||||||
|
);
|
||||||
|
let reg_count = 1usize << reg_num_width;
|
||||||
|
#[hdl]
|
||||||
|
let allocated_reg = reg_builder().clock_domain(cd).reset(vec![false; reg_count]);
|
||||||
|
for free_index in 0..free_at_once.get() {
|
||||||
|
connect(free_in[free_index].ready, true);
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(free_num) = ReadyValid::firing_data(free_in[free_index]) {
|
||||||
|
connect(allocated_reg[free_num], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct Summary {
|
||||||
|
range: Range<usize>,
|
||||||
|
count: Expr<UInt>,
|
||||||
|
count_overflowed: Expr<Bool>,
|
||||||
|
alloc_nums: Expr<Array<UInt>>,
|
||||||
|
}
|
||||||
|
let count_ty = UInt::range_inclusive(0..=alloc_at_once.get());
|
||||||
|
let Some(Summary {
|
||||||
|
range: _,
|
||||||
|
count,
|
||||||
|
count_overflowed,
|
||||||
|
alloc_nums,
|
||||||
|
}) = tree_reduce(
|
||||||
|
(0..reg_count).map(|index| Summary {
|
||||||
|
range: index..index + 1,
|
||||||
|
count: allocated_reg[index]
|
||||||
|
.cast_to_static::<UInt<1>>()
|
||||||
|
.cast_to(count_ty),
|
||||||
|
count_overflowed: false.to_expr(),
|
||||||
|
alloc_nums: repeat(UInt[0].zero(), alloc_at_once.get()),
|
||||||
|
}),
|
||||||
|
|l, r| {
|
||||||
|
let range = l.range.start..r.range.end;
|
||||||
|
#[hdl]
|
||||||
|
let reduced_count = wire(count_ty);
|
||||||
|
connect_any(reduced_count, l.count + r.count);
|
||||||
|
#[hdl]
|
||||||
|
let reduced_count_overflowed = wire();
|
||||||
|
connect(
|
||||||
|
reduced_count_overflowed,
|
||||||
|
(l.count + r.count).cmp_ne(reduced_count) | l.count_overflowed | r.count_overflowed,
|
||||||
|
);
|
||||||
|
#[hdl]
|
||||||
|
let reduced_alloc_nums = wire(
|
||||||
|
Array[UInt[Expr::ty(l.alloc_nums).element().width() + 1]][alloc_at_once.get()],
|
||||||
|
);
|
||||||
|
for alloc_index in 0..alloc_at_once.get() {
|
||||||
|
#[hdl]
|
||||||
|
if l.count_overflowed | l.count.cmp_ge(alloc_at_once) {
|
||||||
|
connect(reduced_alloc_nums[alloc_index], l.alloc_nums[alloc_index]);
|
||||||
|
} else {
|
||||||
|
connect(reduced_alloc_nums[alloc_index], r.alloc_nums[alloc_index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Summary {
|
||||||
|
range,
|
||||||
|
count: reduced_count,
|
||||||
|
count_overflowed: reduced_count_overflowed,
|
||||||
|
alloc_nums: reduced_alloc_nums,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
unreachable!("reg_count is known to be non-zero");
|
||||||
|
};
|
||||||
|
for alloc_index in 0..alloc_at_once.get() {
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(alloc_num) = ReadyValid::firing_data(alloc_out[alloc_index]) {
|
||||||
|
connect(allocated_reg[alloc_num], true);
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
if count_overflowed | count.cmp_ge(alloc_index) {
|
||||||
|
connect(
|
||||||
|
alloc_out[alloc_index].data,
|
||||||
|
HdlSome(alloc_nums[alloc_index]),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
connect(
|
||||||
|
alloc_out[alloc_index].data,
|
||||||
|
HdlOption[UInt[reg_num_width]].HdlNone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add formal proof of unit_free_regs_tracker
|
277
crates/cpu/src/unit.rs
Normal file
277
crates/cpu/src/unit.rs
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::CpuConfig,
|
||||||
|
instruction::{AluBranchMOp, L2RegisterFileMOp, LoadStoreMOp, PRegNum},
|
||||||
|
register::PRegValue,
|
||||||
|
};
|
||||||
|
use fayalite::{
|
||||||
|
bundle::{Bundle, BundleType},
|
||||||
|
intern::{Intern, Interned},
|
||||||
|
prelude::*,
|
||||||
|
util::ready_valid::ReadyValid,
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! all_units {
|
||||||
|
(
|
||||||
|
#[hdl_unit_kind = $HdlUnitKind:ident]
|
||||||
|
#[unit_kind = $UnitKind:ident]
|
||||||
|
#[hdl]
|
||||||
|
$(#[$enum_meta:meta])*
|
||||||
|
$vis:vis enum $UnitMOpEnum:ident<$RegWidth:ident: Size> {
|
||||||
|
$(
|
||||||
|
$(#[$variant_meta:meta])*
|
||||||
|
$Unit:ident($Op:ty),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
$(#[$enum_meta])*
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
|
$vis enum $UnitKind {
|
||||||
|
$(
|
||||||
|
$(#[$variant_meta])*
|
||||||
|
$Unit,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToExpr for $UnitKind {
|
||||||
|
type Type = $HdlUnitKind;
|
||||||
|
|
||||||
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
|
match self {
|
||||||
|
$($UnitKind::$Unit => $HdlUnitKind.$Unit(),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
$(#[$enum_meta])*
|
||||||
|
$vis enum $HdlUnitKind {
|
||||||
|
$(
|
||||||
|
$(#[$variant_meta])*
|
||||||
|
$Unit,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
$(#[$enum_meta])*
|
||||||
|
$vis enum $UnitMOpEnum<$RegWidth: Size> {
|
||||||
|
$(
|
||||||
|
$(#[$variant_meta])*
|
||||||
|
$Unit($Op),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<$RegWidth: Size> $UnitMOpEnum<$RegWidth> {
|
||||||
|
#[hdl]
|
||||||
|
$vis fn kind(expr: impl ToExpr<Type = Self>) -> Expr<$HdlUnitKind> {
|
||||||
|
#[hdl]
|
||||||
|
let unit_kind = wire();
|
||||||
|
#[hdl]
|
||||||
|
match expr {
|
||||||
|
$($UnitMOpEnum::<$RegWidth>::$Unit(_) => connect(unit_kind, $HdlUnitKind.$Unit()),)*
|
||||||
|
}
|
||||||
|
unit_kind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuConfig {
|
||||||
|
#[hdl]
|
||||||
|
pub fn available_units_for_kind(&self, unit_kind: impl ToExpr<Type = $HdlUnitKind>) -> Expr<Array<Bool>> {
|
||||||
|
#[hdl]
|
||||||
|
let available_units_for_kind = wire(Array[Bool][self.unit_kinds.len()]);
|
||||||
|
#[hdl]
|
||||||
|
match unit_kind {
|
||||||
|
$($HdlUnitKind::$Unit => for (index, &unit_kind) in self.unit_kinds.iter().enumerate() {
|
||||||
|
connect(available_units_for_kind[index], unit_kind == $UnitKind::$Unit);
|
||||||
|
})*
|
||||||
|
}
|
||||||
|
available_units_for_kind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
all_units! {
|
||||||
|
#[hdl_unit_kind = HdlUnitKind]
|
||||||
|
#[unit_kind = UnitKind]
|
||||||
|
#[hdl]
|
||||||
|
pub enum UnitMOp<RegWidth: Size> {
|
||||||
|
AluBranch(AluBranchMOp<RegWidth>),
|
||||||
|
L2RegisterFile(L2RegisterFileMOp<RegWidth>),
|
||||||
|
LoadStore(LoadStoreMOp<RegWidth>),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct UnitResultCompleted<ExtraOut> {
|
||||||
|
pub value: PRegValue,
|
||||||
|
pub extra_out: ExtraOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct TrapData {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub enum UnitResult<ExtraOut> {
|
||||||
|
Completed(UnitResultCompleted<ExtraOut>),
|
||||||
|
Trap(TrapData),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct UnitOutput<UnitNumWidth: Size, OutRegNumWidth: Size, ExtraOut> {
|
||||||
|
pub which: PRegNum<UnitNumWidth, OutRegNumWidth>,
|
||||||
|
pub result: UnitResult<ExtraOut>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct UnitCancelInput<UnitNumWidth: Size, OutRegNumWidth: Size> {
|
||||||
|
pub which: PRegNum<UnitNumWidth, OutRegNumWidth>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UnitTrait:
|
||||||
|
'static + Send + Sync + std::fmt::Debug + fayalite::intern::SupportsPtrEqWithTypeId
|
||||||
|
{
|
||||||
|
type Type: BundleType;
|
||||||
|
type ExtraOut: Type;
|
||||||
|
type MOp: Type;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type;
|
||||||
|
fn extra_out_ty(&self) -> Self::ExtraOut;
|
||||||
|
fn mop_ty(&self) -> Self::MOp;
|
||||||
|
|
||||||
|
fn unit_kind(&self) -> UnitKind;
|
||||||
|
|
||||||
|
fn make_module(&self) -> Interned<Module<Self::Type>>;
|
||||||
|
|
||||||
|
// TODO: add other inputs
|
||||||
|
fn cancel_input(
|
||||||
|
&self,
|
||||||
|
this: Expr<Self::Type>,
|
||||||
|
) -> Expr<ReadyValid<UnitCancelInput<DynSize, DynSize>>>;
|
||||||
|
fn output(
|
||||||
|
&self,
|
||||||
|
this: Expr<Self::Type>,
|
||||||
|
) -> Expr<ReadyValid<UnitOutput<DynSize, DynSize, Self::ExtraOut>>>;
|
||||||
|
|
||||||
|
fn to_dyn(&self) -> DynUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynUnitTrait = dyn UnitTrait<Type = Bundle, ExtraOut = CanonicalType, MOp = CanonicalType>;
|
||||||
|
|
||||||
|
impl fayalite::intern::InternedCompare for DynUnitTrait {
|
||||||
|
type InternedCompareKey = fayalite::intern::PtrEqWithTypeId;
|
||||||
|
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
|
||||||
|
Self::get_ptr_eq_with_type_id(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct DynUnit {
|
||||||
|
ty: Bundle,
|
||||||
|
extra_out_ty: CanonicalType,
|
||||||
|
mop_ty: CanonicalType,
|
||||||
|
unit_kind: UnitKind,
|
||||||
|
unit: Interned<DynUnitTrait>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnitTrait for DynUnit {
|
||||||
|
type Type = Bundle;
|
||||||
|
type ExtraOut = CanonicalType;
|
||||||
|
type MOp = CanonicalType;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
self.ty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_out_ty(&self) -> Self::ExtraOut {
|
||||||
|
self.extra_out_ty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mop_ty(&self) -> Self::MOp {
|
||||||
|
self.mop_ty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unit_kind(&self) -> UnitKind {
|
||||||
|
self.unit_kind
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_module(&self) -> Interned<Module<Self::Type>> {
|
||||||
|
self.unit.make_module()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel_input(
|
||||||
|
&self,
|
||||||
|
this: Expr<Self::Type>,
|
||||||
|
) -> Expr<ReadyValid<UnitCancelInput<DynSize, DynSize>>> {
|
||||||
|
self.unit.cancel_input(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output(
|
||||||
|
&self,
|
||||||
|
this: Expr<Self::Type>,
|
||||||
|
) -> Expr<ReadyValid<UnitOutput<DynSize, DynSize, Self::ExtraOut>>> {
|
||||||
|
self.unit.output(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_dyn(&self) -> DynUnit {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct DynUnitWrapper<T>(pub T);
|
||||||
|
|
||||||
|
impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T> {
|
||||||
|
type Type = Bundle;
|
||||||
|
type ExtraOut = CanonicalType;
|
||||||
|
type MOp = CanonicalType;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
Bundle::from_canonical(self.0.ty().canonical())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_out_ty(&self) -> Self::ExtraOut {
|
||||||
|
self.0.extra_out_ty().canonical()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mop_ty(&self) -> Self::MOp {
|
||||||
|
self.0.mop_ty().canonical()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unit_kind(&self) -> UnitKind {
|
||||||
|
self.0.unit_kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_module(&self) -> Interned<Module<Self::Type>> {
|
||||||
|
self.0.make_module().canonical().intern_sized()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel_input(
|
||||||
|
&self,
|
||||||
|
this: Expr<Self::Type>,
|
||||||
|
) -> Expr<ReadyValid<UnitCancelInput<DynSize, DynSize>>> {
|
||||||
|
self.0.cancel_input(Expr::from_bundle(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output(
|
||||||
|
&self,
|
||||||
|
this: Expr<Self::Type>,
|
||||||
|
) -> Expr<ReadyValid<UnitOutput<DynSize, DynSize, Self::ExtraOut>>> {
|
||||||
|
Expr::from_bundle(Expr::as_bundle(self.0.output(Expr::from_bundle(this))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_dyn(&self) -> DynUnit {
|
||||||
|
let unit = self.intern();
|
||||||
|
DynUnit {
|
||||||
|
ty: unit.ty(),
|
||||||
|
extra_out_ty: unit.extra_out_ty(),
|
||||||
|
mop_ty: unit.mop_ty(),
|
||||||
|
unit_kind: unit.unit_kind(),
|
||||||
|
unit: Interned::cast_unchecked(unit, |v: &Self| -> &DynUnitTrait { v }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
pub mod tree_reduce;
|
||||||
|
|
||||||
pub(crate) const fn range_u32_len(range: &std::ops::Range<u32>) -> usize {
|
pub(crate) const fn range_u32_len(range: &std::ops::Range<u32>) -> usize {
|
||||||
let retval = range.end.saturating_sub(range.start);
|
let retval = range.end.saturating_sub(range.start);
|
||||||
assert!(retval as usize as u32 != retval, "len overflowed");
|
assert!(retval as usize as u32 != retval, "len overflowed");
|
||||||
|
|
152
crates/cpu/src/util/tree_reduce.rs
Normal file
152
crates/cpu/src/util/tree_reduce.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
// 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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue