WIP implementing unit_base
All checks were successful
/ deps (push) Successful in 17s
/ test (push) Successful in 25m51s

This commit is contained in:
Jacob Lifshay 2025-02-19 23:54:41 -08:00
parent ece788dda3
commit 3f6e5cc600
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
10 changed files with 17918 additions and 8115 deletions

View file

@ -1,8 +1,11 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
instruction::{PRegNum, UnitNum, UnitOutRegNum, CONST_ZERO_UNIT_NUM},
unit::{unit_base::UnitForwardingInfo, UnitCancelInput, UnitKind, UnitMOp, UnitOutputWrite},
instruction::{MOpTrait, PRegNum, UnitNum, UnitOutRegNum, CONST_ZERO_UNIT_NUM},
unit::{
unit_base::{UnitForwardingInfo, UnitToRegAlloc},
UnitCancelInput, UnitKind, UnitMOp, UnitOutputWrite,
},
};
use fayalite::prelude::*;
use std::num::NonZeroUsize;
@ -95,4 +98,20 @@ impl CpuConfig {
.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()]
}
}

View file

@ -1,7 +1,11 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{unit::UnitMOp, util::range_u32_len};
use fayalite::{expr::ops::ArrayLiteral, intern::Interned, prelude::*};
use fayalite::{
expr::ops::{ArrayLiteral, ExprPartialEq},
intern::Interned,
prelude::*,
};
use std::{fmt, marker::PhantomData, ops::Range};
pub mod power_isa;
@ -12,10 +16,26 @@ pub trait MOpTrait: Type {
type SrcRegWidth: Size;
fn dest_reg_ty(self) -> Self::DestReg;
fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg>;
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType;
fn src_reg_ty(self) -> UIntType<Self::SrcRegWidth> {
UInt[self.src_reg_width()]
}
fn src_regs_ty(self) -> Array<UIntType<Self::SrcRegWidth>, { COMMON_MOP_SRC_LEN }> {
Array[self.src_reg_ty()][ConstUsize::<{ COMMON_MOP_SRC_LEN }>]
}
fn for_each_src_reg(
input: impl ToExpr<Type = Self>,
f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize),
);
fn connect_src_regs(
input: impl ToExpr<Type = Self>,
src_regs: impl ToExpr<Type = Array<UIntType<Self::SrcRegWidth>, { COMMON_MOP_SRC_LEN }>>,
) {
let src_regs = src_regs.to_expr();
Self::for_each_src_reg(input.to_expr(), &mut |src_reg, index| {
connect(src_regs[index], src_reg);
});
}
fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
self,
new_dest_reg: NewDestReg,
@ -67,6 +87,9 @@ impl<T: CommonMOpTrait> MOpTrait for T {
fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg> {
T::common_mop(input).dest
}
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType {
self.common_mop_ty().src.element().width
}
fn for_each_src_reg(
input: impl ToExpr<Type = Self>,
f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize),
@ -129,13 +152,23 @@ pub enum OutputIntegerMode {
SignExt8,
}
impl ExprPartialEq<Self> for OutputIntegerMode {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
}
}
pub const MOP_IMM_WIDTH: usize = 34;
pub const MOP_MIN_REG_WIDTH: usize = 8;
pub const COMMON_MOP_SRC_LEN: usize = 3;
pub const COMMON_MOP_MIN_SRC_LEN_WITH_FULL_IMM: usize = 2;
pub const COMMON_MOP_IMM_LOW_WIDTH: usize = CommonMOpWithMaxSrcCount::IMM_WIDTH - 1;
#[hdl]
#[hdl(cmp_eq)]
pub struct CommonMOp<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
pub prefix_pad: UIntType<PrefixPad>,
pub dest: DestReg,
@ -181,7 +214,7 @@ impl<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize
}
}
#[hdl]
#[hdl(cmp_eq)]
pub struct CommonMOpImmParts<ImmInSrcCount: Size> {
// fields must be in this exact order
pub imm_low: UInt<{ COMMON_MOP_IMM_LOW_WIDTH }>,
@ -410,6 +443,9 @@ macro_rules! mop_enum {
}
dest_reg
}
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType {
self.$FirstVariant.src_reg_width()
}
#[hdl]
fn for_each_src_reg(
input: impl ToExpr<Type = Self>,
@ -458,7 +494,7 @@ pub(crate) use mop_enum;
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> AluCommonMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct AluCommonMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
#[common]
pub common: CommonMOp<ConstUsize<0>, DestReg, SrcRegWidth, SrcCount>,
@ -468,7 +504,7 @@ common_mop_struct! {
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> AddSubMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct AddSubMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
#[common]
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, SrcCount>,
@ -481,7 +517,7 @@ common_mop_struct! {
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> LogicalMOp<NewDestReg, NewSrcRegWidth>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct LogicalMOp<DestReg: Type, SrcRegWidth: Size> {
#[common]
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
@ -491,7 +527,7 @@ common_mop_struct! {
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> BranchMOp<NewDestReg, NewSrcRegWidth>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct BranchMOp<DestReg: Type, SrcRegWidth: Size> {
#[common]
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
@ -510,7 +546,7 @@ mop_enum! {
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> ReadL2RegMOp<NewDestReg, NewSrcRegWidth>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct ReadL2RegMOp<DestReg: Type, SrcRegWidth: Size> {
#[common]
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<0>>,
@ -519,7 +555,7 @@ common_mop_struct! {
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> WriteL2RegMOp<NewDestReg, NewSrcRegWidth>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct WriteL2RegMOp<DestReg: Type, SrcRegWidth: Size> {
#[common]
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<1>>,
@ -536,7 +572,7 @@ mop_enum! {
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> LoadStoreCommonMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct LoadStoreCommonMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
#[common]
pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, SrcCount>,
@ -545,7 +581,7 @@ common_mop_struct! {
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> LoadMOp<NewDestReg, NewSrcRegWidth>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct LoadMOp<DestReg: Type, SrcRegWidth: Size> {
#[common]
pub load_store_common: LoadStoreCommonMOp<DestReg, SrcRegWidth, ConstUsize<1>>,
@ -554,7 +590,7 @@ common_mop_struct! {
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> StoreMOp<NewDestReg, NewSrcRegWidth>)]
#[hdl]
#[hdl(cmp_eq)]
pub struct StoreMOp<DestReg: Type, SrcRegWidth: Size> {
#[common]
pub load_store_common: LoadStoreCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>,
@ -569,7 +605,7 @@ mop_enum! {
}
}
#[hdl]
#[hdl(cmp_eq)]
/// 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> {
@ -617,12 +653,12 @@ impl<Width: Size> UnitNum<Width> {
pub const CONST_ZERO_UNIT_NUM: usize = 0;
#[hdl]
#[hdl(cmp_eq)]
pub struct UnitOutRegNum<Width: Size> {
pub value: UIntType<Width>,
}
#[hdl]
#[hdl(cmp_eq)]
/// Physical Register Number -- registers in the CPU's backend
pub struct PRegNum<UnitNumWidth: Size, OutRegNumWidth: Size> {
pub unit_num: UnitNum<UnitNumWidth>,
@ -643,7 +679,7 @@ impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWi
}
}
#[hdl]
#[hdl(cmp_eq)]
/// µOp Register Number -- register in a micro-operation before register renaming
#[doc(alias = "UOpRegNum")] // help you find it in the docs if you mis-spell it
#[doc(alias = "\u{B5}OpRegNum")] // micro sign
@ -681,7 +717,7 @@ impl MOpRegNum {
Self::CONST_ZERO_REG_NUM + 1..Self::SPECIAL_REG_NUMS.start;
}
#[hdl]
#[hdl(cmp_eq)]
/// all the registers this instruction will write to, they are all renamed to the same physical register.
pub struct MOpDestReg {
/// some instructions have multiple destination registers, e.g. x86 div

View file

@ -4,22 +4,22 @@ use crate::{instruction::MOpRegNum, util::range_u32_nth_or_panic};
use fayalite::prelude::*;
use std::ops::Range;
#[hdl]
#[hdl(cmp_eq)]
pub struct PowerIsaRegNum {
pub value: UInt<5>,
}
#[hdl]
#[hdl(cmp_eq)]
pub struct PowerIsaFRegNum {
pub value: UInt<5>,
}
#[hdl]
#[hdl(cmp_eq)]
pub struct PowerIsaCrFieldNum {
pub value: UInt<3>,
}
#[hdl]
#[hdl(cmp_eq)]
pub struct PowerIsaCrBitNum {
pub cr_field: PowerIsaCrFieldNum,
pub bit_in_field: UInt<2>,

View file

@ -311,7 +311,7 @@ pub fn reg_alloc(config: &CpuConfig) {
SourceLocation::caller(),
);
connect(dyn_unit.cd(unit), cd);
let unit_input_insn = dyn_unit.input_insn(unit);
let unit_to_reg_alloc = dyn_unit.unit_to_reg_alloc(unit);
// TODO: handle assigning multiple instructions to a unit at a time
let assign_to_unit_at_once = NonZeroUsize::new(1).unwrap();
// TODO: handle retiring multiple instructions from a unit at a time
@ -333,8 +333,8 @@ pub fn reg_alloc(config: &CpuConfig) {
);
connect(unit_free_regs_tracker.alloc_out[0].ready, false);
connect(
unit_input_insn.data,
Expr::ty(unit_input_insn).data.HdlNone(),
unit_to_reg_alloc.input_insn.data,
Expr::ty(unit_to_reg_alloc.input_insn).data.HdlNone(),
);
for fetch_index in 0..config.fetch_width.get() {
#[hdl]
@ -343,7 +343,7 @@ pub fn reg_alloc(config: &CpuConfig) {
connect(available_units[fetch_index][unit_index], false);
}
#[hdl]
if !unit_input_insn.ready {
if !unit_to_reg_alloc.input_insn.ready {
// must come after to override connects in loops above
connect(available_units[fetch_index][unit_index], false);
}
@ -356,11 +356,11 @@ pub fn reg_alloc(config: &CpuConfig) {
if let HdlSome(renamed_mop) =
HdlOption::and_then(renamed_mops[fetch_index], |v| dyn_unit.extract_mop(v))
{
connect(unit_input_insn.data, HdlSome(renamed_mop));
connect(unit_to_reg_alloc.input_insn.data, HdlSome(renamed_mop));
} else {
connect(
unit_input_insn.data,
HdlSome(Expr::ty(unit_input_insn).data.HdlSome.uninit()),
unit_to_reg_alloc.input_insn.data,
HdlSome(Expr::ty(unit_to_reg_alloc.input_insn).data.HdlSome.uninit()),
);
// FIXME: add hdl_assert(cd.clk, false.to_expr(), "");
}
@ -387,7 +387,7 @@ pub fn reg_alloc(config: &CpuConfig) {
}
// TODO: connect outputs to other units
connect(
dyn_unit.unit_forwarding_info(unit),
unit_to_reg_alloc.unit_forwarding_info,
#[hdl]
UnitForwardingInfo::<_, _, _> {
unit_output_writes: repeat(
@ -397,10 +397,9 @@ pub fn reg_alloc(config: &CpuConfig) {
_phantom: PhantomData,
},
);
connect(dyn_unit.output(unit).ready, false);
// TODO: handle cancellation
connect(
dyn_unit.cancel_input(unit).data,
unit_to_reg_alloc.cancel_input,
HdlOption[config.unit_cancel_input()].HdlNone(),
);
}

View file

@ -8,7 +8,7 @@ pub enum FlagsMode {
X86(PRegFlagsX86),
}
#[hdl]
#[hdl(cmp_eq)]
pub struct PRegFlagsPowerISA {}
impl PRegFlagsPowerISA {
@ -56,7 +56,7 @@ impl PRegFlagsPowerISA {
}
}
#[hdl]
#[hdl(cmp_eq)]
pub struct PRegFlagsX86 {}
impl PRegFlagsX86 {
@ -100,7 +100,7 @@ impl PRegFlagsX86 {
}
}
#[hdl]
#[hdl(cmp_eq)]
/// this is *not* the same as any particular ISA's flags register,
/// on PowerISA it is a combination of some bits from XER with a single 4-bit CR field.
///
@ -138,7 +138,7 @@ impl PRegFlags {
}
}
#[hdl]
#[hdl(cmp_eq)]
/// Unit output register's value -- a combination of an integer/fp register
/// and flags register and CR field.
///

View file

@ -7,13 +7,12 @@ use crate::{
mop_enum, AluBranchMOp, L2RegisterFileMOp, LoadStoreMOp, MOpTrait, UnitOutRegNum,
},
register::PRegValue,
unit::unit_base::UnitForwardingInfo,
unit::unit_base::UnitToRegAlloc,
};
use fayalite::{
bundle::{Bundle, BundleType},
intern::{Intern, Interned},
prelude::*,
util::ready_valid::ReadyValid,
};
pub mod alu_branch;
@ -143,19 +142,19 @@ all_units! {
}
}
#[hdl]
#[hdl(cmp_eq)]
pub struct UnitResultCompleted<ExtraOut> {
pub value: PRegValue,
pub extra_out: ExtraOut,
}
#[hdl]
#[hdl(cmp_eq)]
pub struct UnitOutputWrite<OutRegNumWidth: Size> {
pub which: UnitOutRegNum<OutRegNumWidth>,
pub value: PRegValue,
}
#[hdl]
#[hdl(cmp_eq)]
pub struct TrapData {
// TODO
}
@ -166,13 +165,25 @@ pub enum UnitResult<ExtraOut> {
Trap(TrapData),
}
impl<ExtraOut: Type> UnitResult<ExtraOut> {
pub fn extra_out_ty(self) -> ExtraOut {
self.Completed.extra_out
}
}
#[hdl]
pub struct UnitOutput<OutRegNumWidth: Size, ExtraOut> {
pub which: UnitOutRegNum<OutRegNumWidth>,
pub result: UnitResult<ExtraOut>,
}
#[hdl]
impl<OutRegNumWidth: Size, ExtraOut: Type> UnitOutput<OutRegNumWidth, 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>,
}
@ -197,19 +208,10 @@ pub trait UnitTrait:
fn module(&self) -> Interned<Module<Self::Type>>;
fn input_insn(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>>;
fn cancel_input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<UnitCancelInput<DynSize>>>;
fn unit_forwarding_info(
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitForwardingInfo<DynSize, DynSize, DynSize>>;
fn output(
&self,
this: Expr<Self::Type>,
) -> Expr<ReadyValid<UnitOutput<DynSize, Self::ExtraOut>>>;
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>>;
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>;
@ -266,26 +268,11 @@ impl UnitTrait for DynUnit {
self.unit.module()
}
fn input_insn(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> {
self.unit.input_insn(this)
}
fn cancel_input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<UnitCancelInput<DynSize>>> {
self.unit.cancel_input(this)
}
fn unit_forwarding_info(
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitForwardingInfo<DynSize, DynSize, DynSize>> {
self.unit.unit_forwarding_info(this)
}
fn output(
&self,
this: Expr<Self::Type>,
) -> Expr<ReadyValid<UnitOutput<DynSize, Self::ExtraOut>>> {
self.unit.output(this)
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
self.unit.unit_to_reg_alloc(this)
}
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> {
@ -332,26 +319,13 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T
self.0.module().canonical().intern_sized()
}
fn input_insn(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> {
Expr::from_bundle(Expr::as_bundle(self.0.input_insn(Expr::from_bundle(this))))
}
fn cancel_input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<UnitCancelInput<DynSize>>> {
self.0.cancel_input(Expr::from_bundle(this))
}
fn unit_forwarding_info(
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitForwardingInfo<DynSize, DynSize, DynSize>> {
self.0.unit_forwarding_info(Expr::from_bundle(this))
}
fn output(
&self,
this: Expr<Self::Type>,
) -> Expr<ReadyValid<UnitOutput<DynSize, Self::ExtraOut>>> {
Expr::from_bundle(Expr::as_bundle(self.0.output(Expr::from_bundle(this))))
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
Expr::from_bundle(Expr::as_bundle(
self.0.unit_to_reg_alloc(Expr::from_bundle(this)),
))
}
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> {

View file

@ -5,14 +5,13 @@ use crate::{
config::CpuConfig,
instruction::{AluBranchMOp, UnitOutRegNum},
unit::{
unit_base::{unit_base, UnitForwardingInfo},
DynUnit, DynUnitWrapper, UnitCancelInput, UnitKind, UnitMOp, UnitOutput, UnitTrait,
unit_base::{unit_base, UnitToRegAlloc},
DynUnit, DynUnitWrapper, UnitKind, UnitMOp, UnitTrait,
},
};
use fayalite::{
intern::{Intern, Interned},
prelude::*,
util::ready_valid::ReadyValid,
};
#[hdl_module]
@ -20,30 +19,30 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let input_insn: ReadyValid<AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>> =
m.input(ReadyValid[AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()]]);
#[hdl]
let unit_forwarding_info: UnitForwardingInfo<DynSize, DynSize, DynSize> =
m.input(config.unit_forwarding_info());
#[hdl]
let cancel_input: ReadyValid<UnitCancelInput<DynSize>> =
m.input(ReadyValid[config.unit_cancel_input()]);
#[hdl]
let output: ReadyValid<UnitOutput<DynSize, ()>> =
m.output(ReadyValid[UnitOutput[config.out_reg_num_width][()]]);
let unit_to_reg_alloc: UnitToRegAlloc<
AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>,
(),
DynSize,
DynSize,
DynSize,
> = m.output(config.unit_to_reg_alloc(
AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()],
(),
));
#[hdl]
let unit_base = instance(unit_base(
config,
unit_index,
Expr::ty(input_insn).data.HdlSome,
Expr::ty(unit_to_reg_alloc).input_insn.data.HdlSome,
(),
));
connect(unit_base.input_insn, input_insn);
connect(unit_to_reg_alloc, unit_base.unit_to_reg_alloc);
connect(unit_base.cd, cd);
connect(unit_base.unit_forwarding_info, unit_forwarding_info);
connect(unit_base.cancel_input, cancel_input);
// TODO: finish
connect(unit_base.ready_mop.ready, true);
connect(output.data, Expr::ty(output.data).HdlNone());
connect(unit_base.execute_start.ready, true); // TODO: finish
connect(
unit_base.execute_end,
Expr::ty(unit_base.execute_end).HdlNone(),
); // TODO: finish
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@ -75,7 +74,7 @@ impl UnitTrait for AluBranch {
}
fn mop_ty(&self) -> Self::MOp {
self.module.io_ty().input_insn.data.HdlSome
self.module.io_ty().unit_to_reg_alloc.mop_ty()
}
fn unit_kind(&self) -> UnitKind {
@ -93,26 +92,11 @@ impl UnitTrait for AluBranch {
self.module
}
fn input_insn(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> {
this.input_insn
}
fn cancel_input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<UnitCancelInput<DynSize>>> {
this.cancel_input
}
fn unit_forwarding_info(
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitForwardingInfo<DynSize, DynSize, DynSize>> {
this.unit_forwarding_info
}
fn output(
&self,
this: Expr<Self::Type>,
) -> Expr<ReadyValid<UnitOutput<DynSize, Self::ExtraOut>>> {
this.output
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> {
this.unit_to_reg_alloc
}
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> {

View file

@ -3,9 +3,9 @@
use crate::{
config::CpuConfig,
instruction::{MOpTrait, UnitOutRegNum, COMMON_MOP_SRC_LEN},
instruction::{MOpTrait, PRegNum, UnitOutRegNum, COMMON_MOP_SRC_LEN},
register::PRegValue,
unit::{UnitCancelInput, UnitOutputWrite},
unit::{UnitCancelInput, UnitOutput, UnitOutputWrite},
util::tree_reduce::tree_reduce,
};
use fayalite::{module::wire_with_loc, prelude::*, ty::StaticType, util::ready_valid::ReadyValid};
@ -18,113 +18,439 @@ pub struct UnitForwardingInfo<UnitNumWidth: Size, OutRegNumWidth: Size, UnitCoun
}
#[hdl]
pub struct ReadyMOp<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>> {
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_insn: ReadyValid<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_insn.data.HdlSome
}
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 src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>,
}
#[hdl]
struct InFlightOp<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>> {
pub mop: MOp,
pub src_values: Array<HdlOption<PRegValue>, { COMMON_MOP_SRC_LEN }>,
pub struct ExecuteEnd<OutRegNumWidth: Size, ExtraOut> {
pub unit_output: UnitOutput<OutRegNumWidth, ExtraOut>,
}
#[hdl]
enum InFlightOpState {
Ready,
Running,
CanceledAndRunning,
}
impl InFlightOpState {
fn ready_next_state(canceling: bool, starting: bool, ending: bool) -> Expr<HdlOption<Self>> {
match (canceling, starting, ending) {
(false, false, _) => HdlSome(InFlightOpState.Ready()),
(false, true, false) => HdlSome(InFlightOpState.Running()),
(false, true, true) => HdlNone(),
(true, false, _) => HdlNone(),
(true, true, false) => HdlSome(InFlightOpState.CanceledAndRunning()),
(true, true, true) => HdlNone(),
}
}
fn running_next_state(canceling: bool, _starting: bool, ending: bool) -> Expr<HdlOption<Self>> {
match (canceling, ending) {
(false, false) => HdlSome(InFlightOpState.Running()),
(false, true) => HdlNone(),
(true, false) => HdlSome(InFlightOpState.CanceledAndRunning()),
(true, true) => HdlNone(),
}
}
fn canceled_and_running_next_state(
_canceling: bool,
_starting: bool,
ending: bool,
) -> Expr<HdlOption<Self>> {
if ending {
HdlNone()
} else {
HdlSome(InFlightOpState.CanceledAndRunning())
}
}
/// FIXME: this is working around #[hdl] match not supporting matching values inside structs yet
#[hdl]
fn connect_next_state(
canceling: Expr<Bool>,
starting: Expr<Bool>,
ending: Expr<Bool>,
next_state_fn: fn(canceling: bool, starting: bool, ending: bool) -> Expr<HdlOption<Self>>,
next_state: Expr<HdlOption<Self>>,
) {
#[hdl]
fn recurse<const N: usize>(
exprs: &[Expr<Bool>; N],
bools: &mut [bool; N],
f: &mut impl FnMut(&[bool; N]),
arg_index: usize,
) {
if arg_index < N {
#[hdl]
if exprs[arg_index] {
bools[arg_index] = true;
recurse(exprs, bools, f, arg_index + 1);
} else {
bools[arg_index] = false;
recurse(exprs, bools, f, arg_index + 1);
}
} else {
f(bools);
}
}
recurse(
&[canceling, starting, ending],
&mut [false; 3],
&mut |&[canceling, starting, ending]| {
connect(next_state, next_state_fn(canceling, starting, ending))
},
0,
);
}
}
#[hdl]
struct InFlightOp<MOp: Type> {
state: InFlightOpState,
mop: MOp,
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<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> {
#[hdl]
fn new<MOp: Type>(
op_index: usize,
op_index_ty: UIntType<OpIndexWidth>,
in_flight_op: impl ToExpr<Type = HdlOption<InFlightOp<MOp>>>,
) -> Expr<Self> {
let empty_op_index = wire_with_loc(
&format!("empty_op_index_{op_index}"),
SourceLocation::caller(),
HdlOption[op_index_ty],
);
connect(empty_op_index, HdlOption[op_index_ty].HdlNone());
let ready_op_index = wire_with_loc(
&format!("ready_op_index_{op_index}"),
SourceLocation::caller(),
HdlOption[op_index_ty],
);
connect(ready_op_index, HdlOption[op_index_ty].HdlNone());
#[hdl]
if let HdlSome(in_flight_op) = in_flight_op {
#[hdl]
let InFlightOp::<_> {
state,
mop: _,
src_ready_flags,
} = in_flight_op;
connect(ready_op_index, HdlOption[op_index_ty].HdlNone());
#[hdl]
match state {
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)));
}
}
InFlightOpState::CanceledAndRunning | InFlightOpState::Running => {}
}
} else {
connect(empty_op_index, HdlSome(op_index.cast_to(op_index_ty)));
}
#[hdl]
InFlightOpsSummary::<_> {
empty_op_index,
ready_op_index,
}
}
#[hdl]
fn combine(l: impl ToExpr<Type = Self>, r: impl ToExpr<Type = Self>) -> Expr<Self> {
let l = l.to_expr();
let r = r.to_expr();
#[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),
}
}
}
impl InFlightOpsSummary<DynSize> {
fn summarize<MOp: Type, MaxInFlight: Size>(
in_flight_ops: impl ToExpr<Type = ArrayType<HdlOption<InFlightOp<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(
index_range.map(|i| Self::new(i, index_ty, in_flight_ops[i])),
Self::combine,
)
.expect("in_flight_ops is known to have len > 0")
}
}
#[hdl_module]
pub fn unit_base<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>>(
pub fn unit_base<
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>,
ExtraOut: Type,
>(
config: &CpuConfig,
unit_index: usize,
mop_ty: MOp,
extra_out_ty: ExtraOut,
) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let unit_forwarding_info: UnitForwardingInfo<DynSize, DynSize, DynSize> =
m.input(config.unit_forwarding_info());
let unit_to_reg_alloc: UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> =
m.output(config.unit_to_reg_alloc(mop_ty, extra_out_ty));
#[hdl]
let input_insn: ReadyValid<MOp> = m.input(ReadyValid[mop_ty]);
connect(input_insn.ready, false);
let execute_start: ReadyValid<ExecuteStart<MOp>> = m.output(ReadyValid[ExecuteStart[mop_ty]]);
#[hdl]
let cancel_input: ReadyValid<UnitCancelInput<DynSize>> =
m.input(ReadyValid[config.unit_cancel_input()]);
connect(cancel_input.ready, true);
#[hdl]
let ready_mop: ReadyValid<ReadyMOp<MOp>> = m.output(ReadyValid[ReadyMOp[mop_ty]]);
connect(ready_mop.data, Expr::ty(ready_mop.data).HdlNone());
let execute_end: HdlOption<ExecuteEnd<DynSize, ExtraOut>> =
m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][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];
#[hdl]
let in_flight_ops = reg_builder().clock_domain(cd).reset(repeat(
HdlOption[InFlightOp[mop_ty]].HdlNone(),
max_in_flight,
));
let in_flight_op_index_ty = UInt::range(0..max_in_flight);
let in_flight_ops = reg_builder()
.clock_domain(cd)
.reset(repeat(HdlOption[in_flight_op_ty].HdlNone(), max_in_flight));
let in_flight_ops_summary_value = InFlightOpsSummary::summarize(in_flight_ops);
#[hdl]
let input_index = wire(HdlOption[in_flight_op_index_ty]);
let in_flight_ops_summary = wire(Expr::ty(in_flight_ops_summary_value));
connect(in_flight_ops_summary, in_flight_ops_summary_value);
connect(
input_index,
tree_reduce(
(0..max_in_flight).map(|i| -> Expr<HdlOption<UInt>> {
HdlOption::map(in_flight_ops[i], |_| i.cast_to(in_flight_op_index_ty))
}),
HdlOption::or,
)
.expect("max_in_flight is known to be non-zero"),
unit_to_reg_alloc.input_insn.ready,
HdlOption::is_some(in_flight_ops_summary.empty_op_index),
);
// TODO: connect(execute_start.data, <read_regs>(in_flight_ops_summary.ready_op_index));
connect(
unit_to_reg_alloc.output,
Expr::ty(unit_to_reg_alloc.output).HdlNone(),
); // TODO: finish
#[hdl]
let input_in_flight_op = wire(HdlOption[InFlightOp[mop_ty]]);
connect(input_in_flight_op, Expr::ty(input_in_flight_op).HdlNone());
let input_in_flight_op = wire(HdlOption[in_flight_op_ty]);
connect(input_in_flight_op, HdlOption[in_flight_op_ty].HdlNone());
#[hdl]
if let HdlSome(mop) = ReadyValid::firing_data(input_insn) {
let src_values = wire_with_loc(
"input_in_flight_op_src_values",
if let HdlSome(mop) = ReadyValid::firing_data(unit_to_reg_alloc.input_insn) {
#[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),
);
MOp::connect_src_regs(mop, input_mop_src_regs);
let src_ready_flags = wire_with_loc(
"input_in_flight_op_src_ready_flags",
SourceLocation::caller(),
StaticType::TYPE,
);
connect(
src_values,
[HdlSome(PRegValue::zeroed()); COMMON_MOP_SRC_LEN],
);
MOp::for_each_src_reg(mop, &mut |src_reg, src_index| {
for src_index in 0..COMMON_MOP_SRC_LEN {
connect(
src_ready_flags[src_index],
config
.p_reg_num()
.const_zero()
.cast_to_bits()
.cmp_eq(input_mop_src_regs[src_index]),
);
}
#[hdl]
if unit_to_reg_alloc.cancel_input.cmp_ne(HdlSome(
#[hdl]
if config
.p_reg_num()
.const_zero()
.cast_to_bits()
.cmp_ne(src_reg)
{
connect(src_values[src_index], HdlNone());
}
});
connect(
input_in_flight_op,
HdlSome(
#[hdl]
InFlightOp::<_> { mop, src_values },
),
);
UnitCancelInput::<_> {
which: MOp::dest_reg(mop),
},
)) {
connect(
input_in_flight_op,
HdlSome(
#[hdl]
InFlightOp::<_> {
state: InFlightOpState.Ready(),
mop,
src_ready_flags,
},
),
);
}
#[hdl]
if let HdlSome(empty_op_index) = in_flight_ops_summary.empty_op_index {
connect(in_flight_ops[empty_op_index], input_in_flight_op);
}
}
#[hdl]
let in_flight_op_next_state = wire(Array[HdlOption[InFlightOpState]][max_in_flight]);
#[hdl]
let in_flight_op_next_src_ready_flags =
wire(Array[in_flight_op_ty.src_ready_flags][max_in_flight]);
#[hdl]
let in_flight_op_canceling = wire(Array[Bool][max_in_flight]);
#[hdl]
let in_flight_op_execute_starting = wire(Array[Bool][max_in_flight]);
#[hdl]
let in_flight_op_execute_ending = wire(Array[Bool][max_in_flight]);
for in_flight_op_index in 0..max_in_flight {
connect(
in_flight_op_next_src_ready_flags[in_flight_op_index],
[false; COMMON_MOP_SRC_LEN],
);
connect(in_flight_op_canceling[in_flight_op_index], false);
connect(in_flight_op_execute_starting[in_flight_op_index], false);
connect(in_flight_op_execute_ending[in_flight_op_index], false);
#[hdl]
if let HdlSome(in_flight_op) = in_flight_ops[in_flight_op_index] {
#[hdl]
if let HdlSome(cancel_input) = ReadyValid::firing_data(cancel_input) {
let InFlightOp::<_> {
state,
mop,
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(),
mop_ty.src_regs_ty(),
);
connect(
src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize),
);
MOp::connect_src_regs(mop, src_regs);
connect(
in_flight_op_next_src_ready_flags[in_flight_op_index],
src_ready_flags,
);
let unit_output_writes = unit_to_reg_alloc.unit_forwarding_info.unit_output_writes;
for unit_index in 0..Expr::ty(unit_output_writes).len() {
#[hdl]
let UnitCancelInput::<_> { which } = cancel_input;
#[hdl]
if which.value.cmp_eq(MOp::dest_reg(in_flight_op.mop).value) {
// TODO: if it needs extra time to cancel (e.g. still in pipeline), handle that here
connect(
in_flight_ops[in_flight_op_index],
HdlOption[InFlightOp[mop_ty]].HdlNone(),
);
if let HdlSome(unit_output_write) = 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,
};
for src_index in 0..COMMON_MOP_SRC_LEN {
#[hdl]
if p_reg_num.cast_to_bits().cmp_eq(src_regs[src_index]) {
connect(
in_flight_op_next_src_ready_flags[in_flight_op_index][src_index],
true,
);
}
}
}
}
// TODO: finish
} else if let HdlSome(input_index) = input_index {
connect(input_insn.ready, true);
connect(
in_flight_op_canceling[in_flight_op_index],
unit_to_reg_alloc.cancel_input.cmp_eq(HdlSome(
#[hdl]
UnitCancelInput::<_> { which },
)),
);
#[hdl]
if input_index.cmp_eq(in_flight_op_index) {
connect(in_flight_ops[in_flight_op_index], input_in_flight_op);
if let HdlSome(execute_end) = execute_end {
#[hdl]
let ExecuteEnd::<_, _> { unit_output } = execute_end;
#[hdl]
if which.cmp_eq(unit_output.which) {
connect(in_flight_op_execute_ending[in_flight_op_index], true);
}
}
#[hdl]
if let HdlSome(execute_start) = ReadyValid::firing_data(execute_start) {
#[hdl]
if which.cmp_eq(MOp::dest_reg(execute_start.mop)) {
connect(in_flight_op_execute_starting[in_flight_op_index], true);
}
}
let connect_next_state = |f| {
InFlightOpState::connect_next_state(
in_flight_op_canceling[in_flight_op_index],
in_flight_op_execute_starting[in_flight_op_index],
in_flight_op_execute_ending[in_flight_op_index],
f,
in_flight_op_next_state[in_flight_op_index],
);
};
#[hdl]
match state {
InFlightOpState::Ready => connect_next_state(InFlightOpState::ready_next_state),
InFlightOpState::Running => connect_next_state(InFlightOpState::running_next_state),
InFlightOpState::CanceledAndRunning => {
connect_next_state(InFlightOpState::canceled_and_running_next_state);
}
}
#[hdl]
if let HdlSome(state) = in_flight_op_next_state[in_flight_op_index] {
connect(
in_flight_ops[in_flight_op_index],
HdlSome(
#[hdl]
InFlightOp::<_> {
state,
mop,
src_ready_flags,
},
),
);
} else {
connect(
in_flight_ops[in_flight_op_index],
HdlOption[in_flight_op_ty].HdlNone(),
);
}
} else {
connect(in_flight_op_next_state[in_flight_op_index], HdlNone());
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff