make Unit API work with rename_execute_retire and add a rename_execute_retire test using unit::alu_branch
All checks were successful
/ test (pull_request) Successful in 6m13s
/ test (push) Successful in 7m10s

This commit is contained in:
Jacob Lifshay 2026-05-24 22:23:23 -07:00
parent a88009a303
commit 7151841af5
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
5 changed files with 195199 additions and 1160 deletions

View file

@ -2,14 +2,12 @@
// See Notices.txt for copyright information
use crate::{
config::{CpuConfig, PhantomConstCpuConfig},
config::CpuConfig,
instruction::{
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, PRegNum, RenamedMOp,
UnitOutRegNum, mop_enum,
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, mop_enum,
},
register::{FlagsMode, PRegValue},
unit::unit_base::UnitToRegAlloc,
rename_execute_retire::ExecuteToUnitInterface,
};
use fayalite::{
bundle::{Bundle, BundleType},
@ -20,7 +18,6 @@ use serde::{Deserialize, Serialize};
use std::ops::ControlFlow;
pub mod alu_branch;
pub mod unit_base;
macro_rules! all_units {
(
@ -342,92 +339,23 @@ all_units! {
}
}
#[hdl]
pub struct GlobalState {
pub flags_mode: FlagsMode,
}
#[hdl(cmp_eq)]
pub struct UnitResultCompleted<ExtraOut> {
pub value: PRegValue,
pub extra_out: ExtraOut,
}
#[hdl(cmp_eq, no_static)]
pub struct UnitOutputWrite<C: PhantomConstGet<CpuConfig>> {
pub which: UnitOutRegNum<C>,
pub value: PRegValue,
}
#[hdl(cmp_eq)]
pub struct TrapData {
// TODO
}
#[hdl]
pub enum UnitResult<ExtraOut> {
Completed(UnitResultCompleted<ExtraOut>),
Trap(TrapData),
}
impl<ExtraOut: Type> UnitResult<ExtraOut> {
pub fn extra_out_ty(self) -> ExtraOut {
self.Completed.extra_out
}
}
#[hdl(no_static)]
pub struct UnitOutput<C: PhantomConstGet<CpuConfig>, ExtraOut> {
pub which: UnitOutRegNum<C>,
pub result: UnitResult<ExtraOut>,
}
impl<C: PhantomConstCpuConfig, ExtraOut: Type> UnitOutput<C, ExtraOut> {
pub fn extra_out_ty(self) -> ExtraOut {
self.result.extra_out_ty()
}
}
#[hdl(cmp_eq, no_static)]
pub struct UnitCancelInput<C: PhantomConstGet<CpuConfig>> {
pub which: UnitOutRegNum<C>,
}
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 extract_mop(
&self,
mop: Expr<
RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
>,
) -> Expr<HdlOption<Self::MOp>>;
fn module(&self) -> Interned<Module<Self::Type>>;
fn unit_to_reg_alloc(
fn cd(&self, this: Expr<Self::Type>) -> Option<Expr<ClockDomain>>;
fn from_execute(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>>;
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>;
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState>;
) -> Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>>;
fn to_dyn(&self) -> DynUnit;
}
type DynUnitTrait = dyn UnitTrait<Type = Bundle, ExtraOut = CanonicalType, MOp = CanonicalType>;
type DynUnitTrait = dyn UnitTrait<Type = Bundle>;
impl fayalite::intern::InternedCompare for DynUnitTrait {
type InternedCompareKey = fayalite::intern::PtrEqWithTypeId;
@ -439,59 +367,34 @@ impl fayalite::intern::InternedCompare for DynUnitTrait {
#[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 extract_mop(
&self,
mop: Expr<
RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
>,
) -> Expr<HdlOption<Self::MOp>> {
self.unit.extract_mop(mop)
}
fn module(&self) -> Interned<Module<Self::Type>> {
self.unit.module()
}
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
self.unit.unit_to_reg_alloc(this)
}
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> {
fn cd(&self, this: Expr<Self::Type>) -> Option<Expr<ClockDomain>> {
self.unit.cd(this)
}
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState> {
self.unit.global_state(this)
fn from_execute(
&self,
this: Expr<Self::Type>,
) -> Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>> {
self.unit.from_execute(this)
}
fn to_dyn(&self) -> DynUnit {
@ -504,61 +407,34 @@ 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 extract_mop(
&self,
mop: Expr<
RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
>,
) -> Expr<HdlOption<Self::MOp>> {
Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop)))
}
fn module(&self) -> Interned<Module<Self::Type>> {
self.0.module().canonical().intern_sized()
}
fn unit_to_reg_alloc(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
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> {
fn cd(&self, this: Expr<Self::Type>) -> Option<Expr<ClockDomain>> {
self.0.cd(Expr::from_bundle(this))
}
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState> {
self.0.global_state(Expr::from_bundle(this))
fn from_execute(
&self,
this: Expr<Self::Type>,
) -> Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>> {
self.0.from_execute(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 }),
}

View file

@ -2,40 +2,35 @@
// See Notices.txt for copyright information
use crate::{
config::CpuConfig,
config::{CpuConfig, PhantomConstCpuConfig},
instruction::{
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOpDefaultImm,
CompareMOp, LogicalFlagsMOp, LogicalMOp, MOpTrait, OutputIntegerMode, PRegNum,
ReadSpecialMOp, RenamedMOp, ShiftRotateMOp, UnitOutRegNum,
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
CommonMOpDefaultImm, CompareMOp, ConditionMode, LogicalFlagsMOp, LogicalMOp,
OutputIntegerMode, PRegNum, ReadSpecialMOp, ShiftRotateMOp,
},
next_pc::CallStackOp,
register::{
FlagsMode, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, PRegFlagsX86,
PRegFlagsX86View, PRegValue, ViewUnused,
FlagsMode, PRegFlags, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait,
PRegFlagsX86, PRegFlagsX86View, PRegValue, ViewUnused,
},
unit::{
DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult,
UnitResultCompleted, UnitTrait,
unit_base::{ExecuteEnd, ExecuteStart, UnitToRegAlloc, unit_base},
rename_execute_retire::{
ExecuteToUnitInterface, GlobalState, NextPcPredictorOp, RenamedMOp, UnitCausedCancel,
UnitFinishCauseCancel, UnitInputsReady, UnitOutputReady,
},
unit::{DynUnit, DynUnitWrapper, UnitKind, UnitTrait},
};
use fayalite::{
intern::Interned, module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid,
};
use fayalite::{intern::Interned, module::wire_with_loc, prelude::*};
use std::{collections::HashMap, ops::RangeTo};
#[hdl]
fn add_sub<SrcCount: KnownSize>(
mop: Expr<
AddSubMOp<
UnitOutRegNum<PhantomConst<CpuConfig>>,
PRegNum<PhantomConst<CpuConfig>>,
SrcCount,
>,
>,
fn add_sub<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
mop: Expr<AddSubMOp<PRegNum<C>, PRegNum<C>, SrcCount>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<TraceAsString<PRegValue>> {
#[hdl]
let GlobalState { flags_mode } = global_state;
#[hdl]
let AddSubMOp::<_, _, _> {
alu_common,
@ -238,373 +233,450 @@ fn add_sub<SrcCount: KnownSize>(
);
}
}
#[hdl]
UnitResultCompleted::<_> {
value: #[hdl]
PRegValue { int_fp, flags },
extra_out: (),
}
let retval = #[hdl]
PRegValue { int_fp, flags };
retval.to_trace_as_string()
}
#[hdl]
fn logical_flags(
mop: Expr<
LogicalFlagsMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn logical(
mop: Expr<
LogicalMOp<
UnitOutRegNum<PhantomConst<CpuConfig>>,
PRegNum<PhantomConst<CpuConfig>>,
ConstUsize<2>,
>,
>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn logical_i(
mop: Expr<
LogicalMOp<
UnitOutRegNum<PhantomConst<CpuConfig>>,
PRegNum<PhantomConst<CpuConfig>>,
ConstUsize<1>,
>,
>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn shift_rotate(
mop: Expr<
ShiftRotateMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn compare<SrcCount: KnownSize>(
mop: Expr<
CompareMOp<
UnitOutRegNum<PhantomConst<CpuConfig>>,
PRegNum<PhantomConst<CpuConfig>>,
SrcCount,
>,
>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl]
fn branch<SrcCount: KnownSize>(
mop: Expr<
BranchMOp<
UnitOutRegNum<PhantomConst<CpuConfig>>,
PRegNum<PhantomConst<CpuConfig>>,
SrcCount,
>,
>,
fn logical_flags<C: PhantomConstCpuConfig>(
global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
mop: Expr<LogicalFlagsMOp<PRegNum<C>, PRegNum<C>>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<TraceAsString<PRegValue>> {
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
let GlobalState { flags_mode } = global_state;
// TODO: finish
PRegValue::zeroed().to_trace_as_string()
}
#[hdl]
fn read_special(
mop: Expr<
ReadSpecialMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
>,
fn logical<C: PhantomConstCpuConfig>(
global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
mop: Expr<LogicalMOp<PRegNum<C>, PRegNum<C>, ConstUsize<2>>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<TraceAsString<PRegValue>> {
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
let GlobalState { flags_mode } = global_state;
// TODO: finish
PRegValue::zeroed().to_trace_as_string()
}
#[hdl]
fn logical_i<C: PhantomConstCpuConfig>(
global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
mop: Expr<LogicalMOp<PRegNum<C>, PRegNum<C>, ConstUsize<1>>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<TraceAsString<PRegValue>> {
#[hdl]
let GlobalState { flags_mode } = global_state;
// TODO: finish
PRegValue::zeroed().to_trace_as_string()
}
#[hdl]
fn shift_rotate<C: PhantomConstCpuConfig>(
global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
mop: Expr<ShiftRotateMOp<PRegNum<C>, PRegNum<C>>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<TraceAsString<PRegValue>> {
#[hdl]
let GlobalState { flags_mode } = global_state;
// TODO: finish
PRegValue::zeroed().to_trace_as_string()
}
#[hdl]
fn compare<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
mop: Expr<CompareMOp<PRegNum<C>, PRegNum<C>, SrcCount>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<TraceAsString<PRegValue>> {
#[hdl]
let GlobalState { flags_mode } = global_state;
// TODO: finish
PRegValue::zeroed().to_trace_as_string()
}
#[hdl]
fn branch<C: PhantomConstCpuConfig, SrcCount: KnownSize>(
global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
fallthrough_pc: Expr<UInt<64>>,
predicted_next_pc: Expr<UInt<64>>,
mop: Expr<BranchMOp<PRegNum<C>, PRegNum<C>, SrcCount>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
config: C,
) -> (
Expr<TraceAsString<PRegValue>>,
Expr<NextPcPredictorOp<C>>,
Expr<HdlOption<UnitCausedCancel<C>>>,
) {
#[hdl]
let GlobalState { flags_mode } = global_state;
#[hdl]
let BranchMOp::<_, _, _> {
common,
invert_src0_cond,
src0_cond_mode,
invert_src2_eq_zero,
pc_relative,
is_call,
is_ret,
} = mop;
#[hdl]
let CommonMOp::<_, _, _, _, _> {
prefix_pad: _,
dest: _,
src,
imm,
} = common;
let [src0, src1, src2] = *src_values;
#[hdl]
let has_src0 = wire();
if let Some(src0) = src.as_ref().get(0) {
connect(has_src0, src0.cmp_ne(src0.ty().const_zero()));
} else {
connect(has_src0, false);
}
#[hdl]
let has_src2 = wire();
if let Some(src2) = src.as_ref().get(2) {
connect(has_src2, src2.cmp_ne(src2.ty().const_zero()));
} else {
connect(has_src2, false);
}
#[hdl]
let src2_cond = wire();
#[hdl]
if has_src2 {
#[hdl]
if invert_src2_eq_zero {
connect(src2_cond, src2.int_fp.cmp_ne(0u64));
} else {
connect(src2_cond, src2.int_fp.cmp_eq(0u64));
}
} else {
connect(src2_cond, true);
}
#[hdl]
let src0_cond = wire();
#[hdl]
match flags_mode {
FlagsMode::PowerISA(_) => {
let src0_flags = PRegFlags::view::<PRegFlagsPowerISA>(src0.flags);
#[hdl]
match src0_cond_mode {
ConditionMode::Eq => connect(src0_cond, src0_flags.cr_eq),
ConditionMode::ULt => connect(src0_cond, src0_flags.cr_lt),
ConditionMode::UGt => connect(src0_cond, src0_flags.cr_gt),
ConditionMode::SLt => connect(src0_cond, src0_flags.cr_lt),
ConditionMode::SGt => connect(src0_cond, src0_flags.cr_gt),
ConditionMode::Sign => connect(src0_cond, false),
ConditionMode::Overflow => connect(src0_cond, src0_flags.so),
ConditionMode::Parity => connect(src0_cond, false),
};
}
FlagsMode::X86(_) => {
let src0_flags = PRegFlags::view::<PRegFlagsX86>(src0.flags);
#[hdl]
match src0_cond_mode {
ConditionMode::Eq => connect(src0_cond, src0_flags.zf),
ConditionMode::ULt => connect(src0_cond, src0_flags.cf),
ConditionMode::UGt => connect(src0_cond, !src0_flags.zf & !src0_flags.cf),
ConditionMode::SLt => connect(src0_cond, src0_flags.sf.cmp_ne(src0_flags.of)),
ConditionMode::SGt => connect(
src0_cond,
!src0_flags.zf & src0_flags.sf.cmp_eq(src0_flags.of),
),
ConditionMode::Sign => connect(src0_cond, src0_flags.sf),
ConditionMode::Overflow => connect(src0_cond, src0_flags.of),
ConditionMode::Parity => connect(src0_cond, src0_flags.pf),
};
}
}
let src0_cond = src0_cond ^ invert_src0_cond;
#[hdl]
let pc_or_zero = wire();
#[hdl]
if pc_relative {
connect(pc_or_zero, pc);
} else {
connect(pc_or_zero, 0u64);
}
#[hdl]
let target_pc: UInt<64> = wire();
connect_any(
target_pc,
src1.int_fp
+ CommonMOpDefaultImm::as_sint_dyn(imm).cast_to_static::<UInt<64>>()
+ pc_or_zero,
);
#[hdl]
let cond_br_taken = wire();
connect(cond_br_taken, src0_cond & src2_cond);
#[hdl]
let is_cond = wire();
connect(is_cond, !cond_br_taken | has_src0 | has_src2);
#[hdl]
let next_pc = wire();
#[hdl]
if cond_br_taken {
connect(next_pc, target_pc);
} else {
connect(next_pc, fallthrough_pc);
};
#[hdl]
let cancel = wire(HdlOption[UnitCausedCancel[config]]);
#[hdl]
if next_pc.cmp_ne(predicted_next_pc) {
connect(
cancel,
HdlSome(
#[hdl]
UnitCausedCancel::<C> {
start_at_pc: next_pc,
cancel_after_retire: true,
config,
},
),
);
} else {
connect(cancel, cancel.ty().HdlNone());
};
let fallthrough_pc_value = #[hdl]
PRegValue {
int_fp: fallthrough_pc,
flags: PRegFlags::zeroed_sim(),
};
#[hdl]
let call_stack_op = wire();
#[hdl]
if is_ret {
connect(call_stack_op, CallStackOp.Pop());
} else if is_call {
connect(call_stack_op, CallStackOp.Push(fallthrough_pc));
} else {
connect(call_stack_op, CallStackOp.None());
}
#[hdl]
let cond_br_taken_opt = wire();
#[hdl]
if is_cond {
connect(cond_br_taken_opt, HdlSome(cond_br_taken));
} else {
connect(cond_br_taken_opt, HdlNone());
}
(
fallthrough_pc_value.into_trace_as_string(),
#[hdl]
NextPcPredictorOp::<_> {
call_stack_op,
cond_br_taken: cond_br_taken_opt,
config,
},
cancel,
)
}
#[hdl]
fn read_special<C: PhantomConstCpuConfig>(
global_state: Expr<GlobalState>,
pc: Expr<UInt<64>>,
mop: Expr<ReadSpecialMOp<PRegNum<C>, PRegNum<C>>>,
src_values: Expr<Array<TraceAsString<PRegValue>, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<TraceAsString<PRegValue>> {
#[hdl]
let GlobalState { flags_mode } = global_state;
// TODO: finish
PRegValue::zeroed().to_trace_as_string()
}
#[hdl_module]
pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let unit_to_reg_alloc: UnitToRegAlloc<
PhantomConst<CpuConfig>,
AluBranchMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
(),
> = m.output(UnitToRegAlloc[config][AluBranchMOp[UnitOutRegNum[config]][PRegNum[config]]][()]);
#[hdl]
let global_state: GlobalState = m.input();
let from_execute: ExecuteToUnitInterface<PhantomConst<CpuConfig>> =
m.input(ExecuteToUnitInterface[config]);
assert_eq!(config.get().units[unit_index].kind, UnitKind::AluBranch);
#[hdl]
let unit_base = instance(unit_base(
config,
unit_index,
unit_to_reg_alloc.ty().input.data.HdlSome.mop,
(),
));
connect(unit_to_reg_alloc, unit_base.unit_to_reg_alloc);
connect(unit_base.cd, cd);
connect(unit_base.execute_start.ready, true);
connect(unit_base.execute_end, unit_base.execute_end.ty().HdlNone());
let ExecuteToUnitInterface::<_> {
global_state,
enqueue,
inputs_ready,
is_no_longer_speculative: _, // we don't care about being speculative for these instructions
cant_cause_cancel,
output_ready,
finish_cause_cancel,
unit_outputs_ready: _,
cancel_all,
config: _,
} = from_execute;
// we ignore enqueues since we don't need to track order for these instructions
connect(enqueue.ready, true);
#[hdl]
if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) {
if let HdlSome(inputs_ready) = inputs_ready {
#[hdl]
let ExecuteStart::<_, _> {
mop,
pc,
let UnitInputsReady::<_> {
mop: mop_instance,
src_values,
config: _,
} = execute_start;
} = inputs_ready;
#[hdl]
match mop {
AluBranchMOp::<_, _>::AddSub(mop) => connect(
unit_base.execute_end,
HdlSome(
let fallthrough_pc: UInt<64> = wire();
connect_any(fallthrough_pc, mop_instance.pc + mop_instance.size_in_bytes);
#[hdl]
match *mop_instance.mop {
RenamedMOp::<_>::AluBranch(mop) => {
#[hdl]
let dest_value = wire();
#[hdl]
let predictor_op = wire(NextPcPredictorOp[config]);
connect(
predictor_op,
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(add_sub(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
NextPcPredictorOp::<_> {
call_stack_op: CallStackOp.None(),
cond_br_taken: HdlNone(),
config,
},
),
),
AluBranchMOp::<_, _>::AddSubI(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(add_sub(
mop,
pc,
global_state.flags_mode,
src_values,
)),
);
#[hdl]
let caused_cancel = wire(HdlOption[UnitCausedCancel[config]]);
connect(caused_cancel, caused_cancel.ty().HdlNone());
#[hdl]
match mop {
AluBranchMOp::<_, _>::AddSub(mop) => {
connect(
dest_value,
add_sub(global_state, mop_instance.pc, mop, src_values),
);
}
AluBranchMOp::<_, _>::AddSubI(mop) => {
connect(
dest_value,
add_sub(global_state, mop_instance.pc, mop, src_values),
);
}
AluBranchMOp::<_, _>::LogicalFlags(mop) => {
connect(
dest_value,
logical_flags(global_state, mop_instance.pc, mop, src_values),
);
}
AluBranchMOp::<_, _>::Logical(mop) => {
connect(
dest_value,
logical(global_state, mop_instance.pc, mop, src_values),
);
}
AluBranchMOp::<_, _>::LogicalI(mop) => {
connect(
dest_value,
logical_i(global_state, mop_instance.pc, mop, src_values),
);
}
AluBranchMOp::<_, _>::ShiftRotate(mop) => {
connect(
dest_value,
shift_rotate(global_state, mop_instance.pc, mop, src_values),
);
}
AluBranchMOp::<_, _>::Compare(mop) => {
connect(
dest_value,
compare(global_state, mop_instance.pc, mop, src_values),
);
}
AluBranchMOp::<_, _>::CompareI(mop) => {
connect(
dest_value,
compare(global_state, mop_instance.pc, mop, src_values),
);
}
AluBranchMOp::<_, _>::Branch(mop) => {
let (dest_value_, predictor_op_, caused_cancel_) = branch(
global_state,
mop_instance.pc,
fallthrough_pc,
mop_instance.predicted_next_pc,
mop,
src_values,
config,
);
connect(dest_value, dest_value_);
connect(predictor_op, predictor_op_);
connect(caused_cancel, caused_cancel_);
}
AluBranchMOp::<_, _>::BranchI(mop) => {
let (dest_value_, predictor_op_, caused_cancel_) = branch(
global_state,
mop_instance.pc,
fallthrough_pc,
mop_instance.predicted_next_pc,
mop,
src_values,
config,
);
connect(dest_value, dest_value_);
connect(predictor_op, predictor_op_);
connect(caused_cancel, caused_cancel_);
}
AluBranchMOp::<_, _>::ReadSpecial(mop) => {
connect(
dest_value,
read_special(global_state, mop_instance.pc, mop, src_values),
);
}
}
connect(
output_ready,
HdlSome(
#[hdl]
UnitOutputReady::<_> {
id: mop_instance.id,
dest_value,
predictor_op,
},
},
),
),
AluBranchMOp::<_, _>::LogicalFlags(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(logical_flags(
mop,
global_state.flags_mode,
src_values,
)),
),
);
connect(
finish_cause_cancel,
HdlSome(
#[hdl]
UnitFinishCauseCancel::<_> {
id: mop_instance.id,
caused_cancel,
config,
},
},
),
),
AluBranchMOp::<_, _>::Logical(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(logical(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::LogicalI(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(logical_i(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::ShiftRotate(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(shift_rotate(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::Compare(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(compare(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::CompareI(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(compare(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::Branch(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(branch(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::BranchI(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(branch(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::ReadSpecial(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(read_special(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
},
),
),
),
);
}
_ => {
// error
connect(output_ready, output_ready.ty().HdlNone());
connect(finish_cause_cancel, finish_cause_cancel.ty().HdlNone());
}
}
} else {
connect(output_ready, output_ready.ty().HdlNone());
connect(finish_cause_cancel, finish_cause_cancel.ty().HdlNone());
}
// all outputs are immediately ready, so reporting that instructions can't cause cancels is superfluous
connect(cant_cause_cancel, cant_cause_cancel.ty().HdlNone());
// this unit is purely combinational so canceling does nothing
connect(cancel_all.ready, true);
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@ -624,52 +696,28 @@ impl AluBranch {
impl UnitTrait for AluBranch {
type Type = alu_branch;
type ExtraOut = ();
type MOp =
AluBranchMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>;
fn ty(&self) -> Self::Type {
self.module.io_ty()
}
fn extra_out_ty(&self) -> Self::ExtraOut {
()
}
fn mop_ty(&self) -> Self::MOp {
self.module.io_ty().unit_to_reg_alloc.mop_ty()
}
fn unit_kind(&self) -> UnitKind {
UnitKind::AluBranch
}
fn extract_mop(
&self,
mop: Expr<
RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, PRegNum<PhantomConst<CpuConfig>>>,
>,
) -> Expr<HdlOption<Self::MOp>> {
UnitMOp::alu_branch_mop(mop)
}
fn module(&self) -> Interned<Module<Self::Type>> {
self.module
}
fn unit_to_reg_alloc(
fn cd(&self, this: Expr<Self::Type>) -> Option<Expr<ClockDomain>> {
None
}
fn from_execute(
&self,
this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
this.unit_to_reg_alloc
}
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> {
this.cd
}
fn global_state(&self, this: Expr<Self::Type>) -> Expr<GlobalState> {
this.global_state
) -> Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>> {
this.from_execute
}
fn to_dyn(&self) -> DynUnit {

View file

@ -1,611 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{CpuConfig, CpuConfigUnitCount, PhantomConstCpuConfig},
instruction::{COMMON_MOP_SRC_LEN, MOpTrait, PRegNum, UnitNum, UnitOutRegNum},
register::PRegValue,
unit::{UnitCancelInput, UnitOutput, UnitOutputWrite},
util::tree_reduce::tree_reduce,
};
use fayalite::{
memory::splat_mask,
module::{memory_with_loc, wire_with_loc},
prelude::*,
ty::StaticType,
util::ready_valid::ReadyValid,
};
#[hdl(no_static)]
pub struct UnitForwardingInfo<C: PhantomConstGet<CpuConfig>> {
pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<C>>, CpuConfigUnitCount<C>>,
pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<C>>, CpuConfigUnitCount<C>>,
}
#[hdl]
pub struct UnitInput<MOp: Type> {
pub mop: MOp,
pub pc: UInt<64>,
}
#[hdl(no_static)]
pub struct UnitToRegAlloc<C: PhantomConstGet<CpuConfig>, MOp: Type, ExtraOut: Type> {
#[hdl(flip)]
pub unit_forwarding_info: UnitForwardingInfo<C>,
#[hdl(flip)]
pub input: ReadyValid<UnitInput<MOp>>,
#[hdl(flip)]
pub cancel_input: HdlOption<UnitCancelInput<C>>,
pub output: HdlOption<UnitOutput<C, ExtraOut>>,
}
impl<C: PhantomConstCpuConfig, MOp: Type, ExtraOut: Type> UnitToRegAlloc<C, MOp, ExtraOut> {
pub fn mop_ty(self) -> MOp {
self.input.data.HdlSome.mop
}
pub fn extra_out_ty(self) -> ExtraOut {
self.output.HdlSome.extra_out_ty()
}
}
#[hdl(no_static)]
pub struct ExecuteStart<
C: PhantomConstGet<CpuConfig>,
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<C>>,
> {
pub mop: MOp,
pub pc: UInt<64>,
pub src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>,
pub config: C,
}
#[hdl(no_static)]
pub struct ExecuteEnd<C: PhantomConstGet<CpuConfig>, ExtraOut> {
pub unit_output: UnitOutput<C, 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,
pc: UInt<64>,
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: _,
pc: _,
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 = in_flight_ops.ty().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<PhantomConst<CpuConfig>>,
SrcReg = PRegNum<PhantomConst<CpuConfig>>,
>,
ExtraOut: Type,
>(
config: PhantomConst<CpuConfig>,
unit_index: usize,
mop_ty: MOp,
extra_out_ty: ExtraOut,
) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let unit_to_reg_alloc: UnitToRegAlloc<PhantomConst<CpuConfig>, MOp, ExtraOut> =
m.output(UnitToRegAlloc[config][mop_ty][extra_out_ty]);
#[hdl]
let execute_start: ReadyValid<ExecuteStart<PhantomConst<CpuConfig>, MOp>> =
m.output(ReadyValid[ExecuteStart[config][mop_ty]]);
#[hdl]
let execute_end: HdlOption<ExecuteEnd<PhantomConst<CpuConfig>, ExtraOut>> =
m.input(HdlOption[ExecuteEnd[config][extra_out_ty]]);
connect(execute_start.data, execute_start.ty().data.HdlNone());
let max_in_flight = config.get().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[in_flight_op_ty].HdlNone(), max_in_flight));
let in_flight_ops_summary_value = InFlightOpsSummary::summarize(in_flight_ops);
#[hdl]
let in_flight_ops_summary = wire(in_flight_ops_summary_value.ty());
connect(in_flight_ops_summary, in_flight_ops_summary_value);
connect(
unit_to_reg_alloc.input.ready,
HdlOption::is_some(in_flight_ops_summary.empty_op_index),
);
#[hdl]
let UnitForwardingInfo::<_> {
unit_output_writes,
unit_reg_frees,
} = unit_to_reg_alloc.unit_forwarding_info;
#[hdl]
let read_src_regs = wire(mop_ty.src_regs_ty());
connect(
read_src_regs,
repeat(PRegNum[config].const_zero(), ConstUsize),
);
#[hdl]
let read_src_values = wire();
connect(read_src_values, [PRegValue::zeroed(); COMMON_MOP_SRC_LEN]);
#[hdl]
let input_src_regs = wire(mop_ty.src_regs_ty());
connect(
input_src_regs,
repeat(PRegNum[config].const_zero(), ConstUsize),
);
#[hdl]
let input_src_regs_valid = wire();
connect(input_src_regs_valid, [true; COMMON_MOP_SRC_LEN]);
let mut unit_output_regs_valid: Vec<MemBuilder<Bool>> = (0..unit_output_writes.ty().len())
.map(|unit_index| {
let mut mem = memory_with_loc(
&format!("unit_{unit_index}_output_regs_valid"),
Bool,
SourceLocation::caller(),
);
mem.depth(1 << config.get().out_reg_num_width);
mem
})
.collect();
for unit_index in 0..unit_output_writes.ty().len() {
let mut unit_output_regs = memory_with_loc(
&format!("unit_{unit_index}_output_regs"),
PRegValue,
SourceLocation::caller(),
);
unit_output_regs.depth(1 << config.get().out_reg_num_width);
for src_index in 0..COMMON_MOP_SRC_LEN {
let read_port = unit_output_regs.new_read_port();
let p_reg_num = read_src_regs[src_index];
connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
connect(read_port.en, false);
connect(read_port.clk, cd.clk);
#[hdl]
if UnitNum::is_index(p_reg_num.unit_num, unit_index) {
connect(read_port.en, true);
connect(read_src_values[src_index], read_port.data);
}
}
for src_index in 0..COMMON_MOP_SRC_LEN {
let read_port = unit_output_regs_valid[unit_index].new_read_port();
let p_reg_num = input_src_regs[src_index];
connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
connect(read_port.en, false);
connect(read_port.clk, cd.clk);
#[hdl]
if UnitNum::is_index(p_reg_num.unit_num, unit_index) {
connect(read_port.en, true);
connect(input_src_regs_valid[src_index], read_port.data);
}
}
let write_port = unit_output_regs.new_write_port();
connect_any(write_port.addr, 0u8);
connect(write_port.en, false);
connect(write_port.clk, cd.clk);
connect(write_port.data, PRegValue::zeroed());
connect(write_port.mask, splat_mask(PRegValue, true.to_expr()));
let ready_write_port = unit_output_regs_valid[unit_index].new_write_port();
connect_any(ready_write_port.addr, 0u8);
connect(ready_write_port.en, false);
connect(ready_write_port.clk, cd.clk);
connect(ready_write_port.data, true);
connect(ready_write_port.mask, true);
#[hdl]
if let HdlSome(unit_output_write) = unit_output_writes[unit_index] {
connect_any(write_port.addr, unit_output_write.which.value);
connect(write_port.data, unit_output_write.value);
connect(write_port.en, true);
connect_any(ready_write_port.addr, unit_output_write.which.value);
connect(ready_write_port.en, true);
let p_reg_num = #[hdl]
PRegNum::<_> {
unit_num: UnitNum[config].from_index(unit_index),
unit_out_reg: unit_output_write.which,
};
for src_index in 0..COMMON_MOP_SRC_LEN {
#[hdl]
if input_src_regs[src_index].cmp_eq(p_reg_num) {
connect(input_src_regs_valid[src_index], true);
}
}
}
let free_write_port = unit_output_regs_valid[unit_index].new_write_port();
connect_any(free_write_port.addr, 0u8);
connect(free_write_port.en, false);
connect(free_write_port.clk, cd.clk);
connect(free_write_port.data, false);
connect(free_write_port.mask, true);
#[hdl]
if let HdlSome(unit_reg_free) = unit_reg_frees[unit_index] {
connect_any(free_write_port.addr, unit_reg_free.value);
connect(free_write_port.en, true);
}
}
#[hdl]
if let HdlSome(ready_op_index) = in_flight_ops_summary.ready_op_index {
#[hdl]
if let HdlSome(in_flight_op) = in_flight_ops[ready_op_index] {
connect(
execute_start.data,
HdlSome(
#[hdl]
ExecuteStart::<_, _> {
mop: in_flight_op.mop,
pc: in_flight_op.pc,
src_values: read_src_values,
config,
},
),
);
}
}
connect(
unit_to_reg_alloc.output,
unit_to_reg_alloc.output.ty().HdlNone(),
);
#[hdl]
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(input) = ReadyValid::firing_data(unit_to_reg_alloc.input) {
#[hdl]
let UnitInput::<_> { mop, pc } = input;
#[hdl]
let input_mop_src_regs = wire(mop_ty.src_regs_ty());
connect(
input_mop_src_regs,
repeat(PRegNum[config].const_zero(), 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_ready_flags, input_src_regs_valid);
connect(input_src_regs, input_mop_src_regs);
#[hdl]
if unit_to_reg_alloc.cancel_input.cmp_ne(HdlSome(
#[hdl]
UnitCancelInput::<_> {
which: MOp::dest_reg(mop),
},
)) {
connect(
input_in_flight_op,
HdlSome(
#[hdl]
InFlightOp::<_> {
state: InFlightOpState.Ready(),
mop,
pc,
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]
let InFlightOp::<_> {
state,
mop,
pc,
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(PRegNum[config].const_zero(), ConstUsize));
MOp::connect_src_regs(mop, src_regs);
#[hdl]
if in_flight_ops_summary.ready_op_index.cmp_eq(HdlSome(
in_flight_op_index.cast_to(in_flight_ops_summary.ty().ready_op_index.HdlSome),
)) {
connect(read_src_regs, src_regs);
}
connect(
in_flight_op_next_src_ready_flags[in_flight_op_index],
src_ready_flags,
);
for unit_index in 0..unit_output_writes.ty().len() {
#[hdl]
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: UnitNum[config].from_index(unit_index),
unit_out_reg,
};
for src_index in 0..COMMON_MOP_SRC_LEN {
#[hdl]
if p_reg_num.cmp_eq(src_regs[src_index]) {
connect(
in_flight_op_next_src_ready_flags[in_flight_op_index][src_index],
true,
);
}
}
}
}
connect(
in_flight_op_canceling[in_flight_op_index],
unit_to_reg_alloc.cancel_input.cmp_eq(HdlSome(
#[hdl]
UnitCancelInput::<_> { which },
)),
);
#[hdl]
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 !in_flight_op_canceling[in_flight_op_index] {
#[hdl]
match state {
InFlightOpState::Running | InFlightOpState::Ready => {
connect(unit_to_reg_alloc.output, HdlSome(unit_output))
}
InFlightOpState::CanceledAndRunning => {}
}
}
}
}
#[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,
pc,
src_ready_flags: in_flight_op_next_src_ready_flags[in_flight_op_index],
},
),
);
} 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

View file

@ -4057,10 +4057,16 @@ fn mock_load_store_unit<#[hdl(skip)] MI: MakeInsns>(
}
}
enum AluBranchKind {
MockUnit,
MockCombinationalUnit,
Real,
}
#[hdl_module]
fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(
config: PhantomConst<CpuConfig>,
alu_branch_is_combinatorial: bool,
alu_branch_kind: AluBranchKind,
) {
#[hdl]
let cd: ClockDomain = m.input();
@ -4106,15 +4112,16 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(
connect(mock_unit.from_execute, to_unit);
connect(started_any_l2_reg_file_ops, mock_unit.started_any);
}
UnitKind::AluBranch => {
if alu_branch_is_combinatorial {
UnitKind::AluBranch => match alu_branch_kind {
AluBranchKind::MockCombinationalUnit => {
let mock_unit = instance_with_loc(
&dut.ty().to_units.unit_field_name(unit_index),
mock_combinational_unit::<()>(config, unit_index),
SourceLocation::caller(),
);
connect(mock_unit.from_execute, to_unit);
} else {
}
AluBranchKind::MockUnit => {
let mock_unit = instance_with_loc(
&dut.ty().to_units.unit_field_name(unit_index),
mock_unit::<()>(config, unit_index),
@ -4123,7 +4130,15 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(
connect(mock_unit.cd, cd);
connect(mock_unit.from_execute, to_unit);
}
}
AluBranchKind::Real => {
let unit = instance_with_loc(
&dut.ty().to_units.unit_field_name(unit_index),
cpu::unit::alu_branch::alu_branch(config, unit_index),
SourceLocation::caller(),
);
connect(unit.from_execute, to_unit);
}
},
}
}
}
@ -4198,7 +4213,7 @@ fn test_rename_execute_retire_fibonacci_non_combinatorial() {
config.fetch_width = NonZeroUsize::new(3).unwrap();
let m = rename_execute_retire_test_harness::<FibonacciInsns>(
PhantomConst::new_sized(config),
false,
AluBranchKind::MockUnit,
);
let mut sim = Simulation::new(m);
let _checked_vcd_output = checked_vcd_output!(
@ -4233,8 +4248,10 @@ fn test_rename_execute_retire_fibonacci_combinatorial() {
NonZeroUsize::new(20).unwrap(),
);
config.fetch_width = NonZeroUsize::new(3).unwrap();
let m =
rename_execute_retire_test_harness::<FibonacciInsns>(PhantomConst::new_sized(config), true);
let m = rename_execute_retire_test_harness::<FibonacciInsns>(
PhantomConst::new_sized(config),
AluBranchKind::MockCombinationalUnit,
);
let mut sim = Simulation::new(m);
let _checked_vcd_output = checked_vcd_output!(
&mut sim,
@ -4348,8 +4365,10 @@ fn test_rename_execute_retire_slow_loop() {
NonZeroUsize::new(20).unwrap(),
);
config.fetch_width = NonZeroUsize::new(4).unwrap();
let m =
rename_execute_retire_test_harness::<SlowLoopInsns>(PhantomConst::new_sized(config), true);
let m = rename_execute_retire_test_harness::<SlowLoopInsns>(
PhantomConst::new_sized(config),
AluBranchKind::MockCombinationalUnit,
);
let mut sim = Simulation::new(m);
let _checked_vcd_output = checked_vcd_output!(
&mut sim,
@ -4469,8 +4488,10 @@ fn test_rename_execute_retire_head_n1() {
NonZeroUsize::new(20).unwrap(),
);
config.fetch_width = NonZeroUsize::new(2).unwrap();
let m =
rename_execute_retire_test_harness::<HeadN1Insns>(PhantomConst::new_sized(config), true);
let m = rename_execute_retire_test_harness::<HeadN1Insns>(
PhantomConst::new_sized(config),
AluBranchKind::MockCombinationalUnit,
);
let mut sim = Simulation::new(m);
let _checked_vcd_output =
checked_vcd_output!(&mut sim, "tests/expected/rename_execute_retire_head_n1.vcd");
@ -4566,7 +4587,7 @@ fn test_rename_execute_retire_save_restore_gprs() {
config.fetch_width = NonZeroUsize::new(2).unwrap();
let m = rename_execute_retire_test_harness::<SaveRestoreGprsInsns>(
PhantomConst::new_sized(config),
true,
AluBranchKind::MockCombinationalUnit,
);
let mut sim = Simulation::new(m);
let _checked_vcd_output = checked_vcd_output!(
@ -4585,3 +4606,39 @@ fn test_rename_execute_retire_save_restore_gprs() {
}
assert!(sim.read_bool(sim.io().all_outputs_written));
}
#[hdl]
#[test]
fn test_rename_execute_retire_real_alu_branch() {
let _n = SourceLocation::normalize_files_for_tests();
let mut config = CpuConfig::new(
vec![
UnitConfig::new(UnitKind::AluBranch),
UnitConfig::new(UnitKind::AluBranch),
UnitConfig::new(UnitKind::LoadStore),
UnitConfig::new(UnitKind::TransformedMove),
],
NonZeroUsize::new(20).unwrap(),
);
config.fetch_width = NonZeroUsize::new(2).unwrap();
let m = rename_execute_retire_test_harness::<SaveRestoreGprsInsns>(
PhantomConst::new_sized(config),
AluBranchKind::Real,
);
let mut sim = Simulation::new(m);
let _checked_vcd_output = checked_vcd_output!(
&mut sim,
"tests/expected/rename_execute_retire_real_alu_branch.vcd",
);
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
for cycle in 0..700 {
sim.advance_time(SimDuration::from_nanos(500));
println!("clock tick: {cycle}");
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);
}
assert!(sim.read_bool(sim.io().all_outputs_written));
}