WIP adding rename_execute_retire

This commit is contained in:
Jacob Lifshay 2026-04-09 22:23:18 -07:00
parent 6ed04c809e
commit 3a023ffe83
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
12 changed files with 89913 additions and 166 deletions

View file

@ -33,3 +33,6 @@ hex-literal.workspace = true
regex = "1.12.2" regex = "1.12.2"
sha2.workspace = true sha2.workspace = true
which.workspace = true which.workspace = true
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(todo)'] }

View file

@ -1,12 +1,6 @@
// 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::{ use crate::{instruction::CONST_ZERO_UNIT_NUM, unit::UnitKind};
instruction::{CONST_ZERO_UNIT_NUM, MOpTrait, PRegNum, RenamedMOp, UnitNum, UnitOutRegNum},
unit::{
UnitCancelInput, UnitKind, UnitOutputWrite,
unit_base::{UnitForwardingInfo, UnitToRegAlloc},
},
};
use fayalite::prelude::*; use fayalite::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
@ -101,54 +95,20 @@ impl CpuConfig {
pub fn unit_num_width(&self) -> usize { pub fn unit_num_width(&self) -> usize {
UInt::range(CONST_ZERO_UNIT_NUM..self.non_const_unit_nums().end).width() UInt::range(CONST_ZERO_UNIT_NUM..self.non_const_unit_nums().end).width()
} }
pub fn unit_num(&self) -> UnitNum<DynSize> {
UnitNum[self.unit_num_width()]
}
pub fn unit_out_reg_num(&self) -> UnitOutRegNum<DynSize> {
UnitOutRegNum[self.out_reg_num_width]
}
pub fn p_reg_num(&self) -> PRegNum<DynSize, DynSize> {
PRegNum[self.unit_num_width()][self.out_reg_num_width]
}
pub fn p_reg_num_width(&self) -> usize { pub fn p_reg_num_width(&self) -> usize {
self.unit_num_width() + self.out_reg_num_width self.unit_num_width() + self.out_reg_num_width
} }
pub fn renamed_mop_in_unit(&self) -> RenamedMOp<UnitOutRegNum<DynSize>, DynSize> {
RenamedMOp[self.unit_out_reg_num()][self.p_reg_num_width()]
}
pub fn unit_output_write(&self) -> UnitOutputWrite<DynSize> {
UnitOutputWrite[self.out_reg_num_width]
}
pub fn unit_output_writes(&self) -> Array<HdlOption<UnitOutputWrite<DynSize>>> {
Array[HdlOption[self.unit_output_write()]][self.non_const_unit_nums().len()]
}
pub fn unit_cancel_input(&self) -> UnitCancelInput<DynSize> {
UnitCancelInput[self.out_reg_num_width]
}
pub fn unit_forwarding_info(&self) -> UnitForwardingInfo<DynSize, DynSize, DynSize> {
UnitForwardingInfo[self.unit_num_width()][self.out_reg_num_width]
[self.non_const_unit_nums().len()]
}
pub fn unit_max_in_flight(&self, unit_index: usize) -> NonZeroUsize { pub fn unit_max_in_flight(&self, unit_index: usize) -> NonZeroUsize {
self.units[unit_index] self.units[unit_index]
.max_in_flight .max_in_flight
.unwrap_or(self.default_unit_max_in_flight) .unwrap_or(self.default_unit_max_in_flight)
} }
pub fn unit_to_reg_alloc< /// the maximum of all [`unit_max_in_flight()`][Self::unit_max_in_flight()]
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>, pub fn max_unit_max_in_flight(&self) -> NonZeroUsize {
ExtraOut: Type, (0..self.units.len())
>( .map(|unit_index| self.unit_max_in_flight(unit_index))
&self, .max()
mop_ty: MOp, .unwrap_or(self.default_unit_max_in_flight)
extra_out_ty: ExtraOut,
) -> UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> {
assert_eq!(
mop_ty.dest_reg_ty(),
self.unit_out_reg_num(),
"inconsistent types",
);
UnitToRegAlloc[mop_ty][extra_out_ty][self.unit_num_width()][self.out_reg_num_width]
[self.non_const_unit_nums().len()]
} }
pub fn fetch_width_in_bytes(&self) -> usize { pub fn fetch_width_in_bytes(&self) -> usize {
1usize 1usize
@ -188,6 +148,21 @@ impl CpuConfig {
} }
} }
#[hdl(get(|c| c.out_reg_num_width))]
pub type CpuConfigOutRegNumWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.unit_num_width()))]
pub type CpuConfigUnitNumWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.p_reg_num_width()))]
pub type CpuConfigPRegNumWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| 1 << c.out_reg_num_width))]
pub type CpuConfig2PowOutRegNumWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.units.len()))]
pub type CpuConfigUnitCount<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.fetch_width.get()))] #[hdl(get(|c| c.fetch_width.get()))]
pub type CpuConfigFetchWidth<C: PhantomConstGet<CpuConfig>> = DynSize; pub type CpuConfigFetchWidth<C: PhantomConstGet<CpuConfig>> = DynSize;
@ -236,6 +211,10 @@ pub type CpuConfigL1ICacheMaxMissesInFlight<C: PhantomConstGet<CpuConfig>> = Dyn
#[hdl(get(|c| c.rob_size.get()))] #[hdl(get(|c| c.rob_size.get()))]
pub type CpuConfigRobSize<C: PhantomConstGet<CpuConfig>> = DynSize; pub type CpuConfigRobSize<C: PhantomConstGet<CpuConfig>> = DynSize;
/// the maximum of all [`unit_max_in_flight()`][CpuConfig::unit_max_in_flight()]
#[hdl(get(|c| c.max_unit_max_in_flight().get()))]
pub type CpuConfigMaxUnitMaxInFlight<C: PhantomConstGet<CpuConfig>> = DynSize;
pub trait PhantomConstCpuConfig: pub trait PhantomConstCpuConfig:
PhantomConstGet<CpuConfig> PhantomConstGet<CpuConfig>
+ Into<PhantomConst<CpuConfig>> + Into<PhantomConst<CpuConfig>>

View file

@ -1,6 +1,7 @@
// 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::{ use crate::{
config::{CpuConfig, CpuConfigOutRegNumWidth, CpuConfigUnitNumWidth, PhantomConstCpuConfig},
register::{PRegFlags, PRegFlagsViewTrait, PRegValue, ViewUnused}, register::{PRegFlags, PRegFlagsViewTrait, PRegValue, ViewUnused},
unit::UnitMOp, unit::UnitMOp,
util::{Rotate, range_u32_len}, util::{Rotate, range_u32_len},
@ -91,6 +92,9 @@ pub trait MOpTrait: Type {
type SrcRegWidth: Size; type SrcRegWidth: Size;
fn dest_reg_ty(self) -> Self::DestReg; fn dest_reg_ty(self) -> Self::DestReg;
fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg>; fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg>;
fn dest_reg_sim(input: impl ToSimValue<Type = Self>) -> SimValue<Self::DestReg>;
fn dest_reg_sim_ref(input: &SimValue<Self>) -> &SimValue<Self::DestReg>;
fn dest_reg_sim_mut(input: &mut SimValue<Self>) -> &mut SimValue<Self::DestReg>;
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType; fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType;
fn src_reg_ty(self) -> UIntType<Self::SrcRegWidth> { fn src_reg_ty(self) -> UIntType<Self::SrcRegWidth> {
UInt[self.src_reg_width()] UInt[self.src_reg_width()]
@ -102,6 +106,18 @@ pub trait MOpTrait: Type {
input: impl ToExpr<Type = Self>, input: impl ToExpr<Type = Self>,
f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize), f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize),
); );
fn for_each_src_reg_sim(
input: SimValue<Self>,
f: &mut impl FnMut(SimValue<UIntType<Self::SrcRegWidth>>, usize),
);
fn for_each_src_reg_sim_ref(
input: &SimValue<Self>,
f: &mut impl FnMut(&SimValue<UIntType<Self::SrcRegWidth>>, usize),
);
fn for_each_src_reg_sim_mut(
input: &mut SimValue<Self>,
f: &mut impl FnMut(&mut SimValue<UIntType<Self::SrcRegWidth>>, usize),
);
fn connect_src_regs( fn connect_src_regs(
input: impl ToExpr<Type = Self>, input: impl ToExpr<Type = Self>,
src_regs: impl ToExpr<Type = Array<UIntType<Self::SrcRegWidth>, { COMMON_MOP_SRC_LEN }>>, src_regs: impl ToExpr<Type = Array<UIntType<Self::SrcRegWidth>, { COMMON_MOP_SRC_LEN }>>,
@ -125,6 +141,15 @@ pub trait MOpTrait: Type {
usize, usize,
) -> Expr<UIntType<NewSrcRegWidth>>, ) -> Expr<UIntType<NewSrcRegWidth>>,
) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>>; ) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>>;
fn map_regs_sim<NewDestReg: Type, NewSrcRegWidth: Size>(
input: impl ToSimValue<Type = Self>,
new_dest: impl ToSimValue<Type = NewDestReg>,
new_src_reg_width: NewSrcRegWidth::SizeType,
map_src: &mut impl FnMut(
SimValue<UIntType<Self::SrcRegWidth>>,
usize,
) -> SimValue<UIntType<NewSrcRegWidth>>,
) -> SimValue<Self::Mapped<NewDestReg, NewSrcRegWidth>>;
} }
pub trait CommonMOpTrait: MOpTrait { pub trait CommonMOpTrait: MOpTrait {
@ -146,6 +171,21 @@ pub trait CommonMOpTrait: MOpTrait {
fn common_mop( fn common_mop(
input: impl ToExpr<Type = Self>, input: impl ToExpr<Type = Self>,
) -> Expr<CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>>; ) -> Expr<CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>>;
fn common_mop_sim(
input: impl ToSimValue<Type = Self>,
) -> SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
>;
fn common_mop_sim_ref(
input: &SimValue<Self>,
) -> &SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
>;
fn common_mop_sim_mut(
input: &mut SimValue<Self>,
) -> &mut SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
>;
fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>( fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
self, self,
new_common_mop_ty: CommonMOp< new_common_mop_ty: CommonMOp<
@ -168,6 +208,18 @@ pub trait CommonMOpTrait: MOpTrait {
>, >,
>, >,
) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>>; ) -> Expr<Self::Mapped<NewDestReg, NewSrcRegWidth>>;
fn with_common_mop_sim<NewDestReg: Type, NewSrcRegWidth: Size>(
input: impl ToSimValue<Type = Self>,
new_common_mop: impl ToSimValue<
Type = CommonMOp<
Self::PrefixPad,
NewDestReg,
NewSrcRegWidth,
Self::SrcCount,
Self::Imm,
>,
>,
) -> SimValue<Self::Mapped<NewDestReg, NewSrcRegWidth>>;
} }
pub type CommonMOpFor<T> = CommonMOp< pub type CommonMOpFor<T> = CommonMOp<
@ -189,6 +241,15 @@ impl<T: CommonMOpTrait> MOpTrait for T {
fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg> { fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg> {
T::common_mop(input).dest T::common_mop(input).dest
} }
fn dest_reg_sim(input: impl ToSimValue<Type = Self>) -> SimValue<Self::DestReg> {
SimValue::into_value(T::common_mop_sim(input)).dest
}
fn dest_reg_sim_ref(input: &SimValue<Self>) -> &SimValue<Self::DestReg> {
&T::common_mop_sim_ref(input).dest
}
fn dest_reg_sim_mut(input: &mut SimValue<Self>) -> &mut SimValue<Self::DestReg> {
&mut T::common_mop_sim_mut(input).dest
}
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType { fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType {
self.common_mop_ty().src.element().width self.common_mop_ty().src.element().width
} }
@ -202,6 +263,37 @@ impl<T: CommonMOpTrait> MOpTrait for T {
f(common.src[index], index); f(common.src[index], index);
} }
} }
fn for_each_src_reg_sim(
input: SimValue<Self>,
f: &mut impl FnMut(SimValue<UIntType<Self::SrcRegWidth>>, usize),
) {
let common = SimValue::into_value(T::common_mop_sim(input));
for (index, src) in SimValue::into_value(common.src)
.into_iter()
.take(T::SrcCount::VALUE)
.enumerate()
{
f(src, index);
}
}
fn for_each_src_reg_sim_ref(
input: &SimValue<Self>,
f: &mut impl FnMut(&SimValue<UIntType<Self::SrcRegWidth>>, usize),
) {
let common = T::common_mop_sim_ref(input);
for index in 0..T::SrcCount::VALUE {
f(&common.src[index], index);
}
}
fn for_each_src_reg_sim_mut(
input: &mut SimValue<Self>,
f: &mut impl FnMut(&mut SimValue<UIntType<Self::SrcRegWidth>>, usize),
) {
let common = T::common_mop_sim_mut(input);
for index in 0..T::SrcCount::VALUE {
f(&mut common.src[index], index);
}
}
fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>( fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
self, self,
new_dest_reg: NewDestReg, new_dest_reg: NewDestReg,
@ -244,6 +336,30 @@ impl<T: CommonMOpTrait> MOpTrait for T {
}, },
) )
} }
#[hdl]
fn map_regs_sim<NewDestReg: Type, NewSrcRegWidth: Size>(
input: impl ToSimValue<Type = Self>,
new_dest: impl ToSimValue<Type = NewDestReg>,
new_src_reg_width: NewSrcRegWidth::SizeType,
map_src: &mut impl FnMut(
SimValue<UIntType<Self::SrcRegWidth>>,
usize,
) -> SimValue<UIntType<NewSrcRegWidth>>,
) -> SimValue<Self::Mapped<NewDestReg, NewSrcRegWidth>> {
let input = input.into_sim_value();
let common = T::common_mop_sim_ref(&input);
let common = #[hdl(sim)]
CommonMOp::<_, _, _, _, _> {
prefix_pad: &common.prefix_pad,
dest: new_dest,
src: SimValue::from_array_elements(
ArrayType[UIntType[new_src_reg_width]][T::SrcCount::SIZE],
(0..T::SrcCount::VALUE).map(|index| map_src(common.src[index].clone(), index)),
),
imm: common.imm,
};
T::with_common_mop_sim(input, common)
}
} }
impl<T: CommonMOpTrait> MOpVisitVariants for T { impl<T: CommonMOpTrait> MOpVisitVariants for T {
@ -532,6 +648,27 @@ impl<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize
input.ty().validate(); input.ty().validate();
input input
} }
fn common_mop_sim(
input: impl ToSimValue<Type = Self>,
) -> SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
> {
input.into_sim_value()
}
fn common_mop_sim_ref(
input: &SimValue<Self>,
) -> &SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
> {
input
}
fn common_mop_sim_mut(
input: &mut SimValue<Self>,
) -> &mut SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
> {
input
}
fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>( fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
self, self,
new_common_mop_ty: CommonMOp< new_common_mop_ty: CommonMOp<
@ -564,6 +701,24 @@ impl<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize
new_common_mop.ty().validate(); new_common_mop.ty().validate();
new_common_mop new_common_mop
} }
fn with_common_mop_sim<NewDestReg: Type, NewSrcRegWidth: Size>(
input: impl ToSimValue<Type = Self>,
new_common_mop: impl ToSimValue<
Type = CommonMOp<
Self::PrefixPad,
NewDestReg,
NewSrcRegWidth,
Self::SrcCount,
Self::Imm,
>,
>,
) -> SimValue<Self::Mapped<NewDestReg, NewSrcRegWidth>> {
let input = input.into_sim_value();
let new_common_mop = new_common_mop.into_sim_value();
input.ty().validate();
new_common_mop.ty().validate();
new_common_mop
}
} }
pub const COMMON_MOP_0_IMM_WIDTH: usize = common_mop_max_imm_size(0); pub const COMMON_MOP_0_IMM_WIDTH: usize = common_mop_max_imm_size(0);
@ -613,6 +768,27 @@ macro_rules! common_mop_struct {
) -> Expr<CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>> { ) -> Expr<CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>> {
CommonMOpTrait::common_mop(input.to_expr().$common) CommonMOpTrait::common_mop(input.to_expr().$common)
} }
fn common_mop_sim(
input: impl ToSimValue<Type = Self>,
) -> SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
> {
CommonMOpTrait::common_mop_sim(SimValue::into_value(input.into_sim_value()).$common)
}
fn common_mop_sim_ref(
input: &SimValue<Self>,
) -> &SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
> {
CommonMOpTrait::common_mop_sim_ref(&input.$common)
}
fn common_mop_sim_mut(
input: &mut SimValue<Self>,
) -> &mut SimValue<
CommonMOp<Self::PrefixPad, Self::DestReg, Self::SrcRegWidth, Self::SrcCount, Self::Imm>,
> {
CommonMOpTrait::common_mop_sim_mut(&mut input.$common)
}
fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>( fn with_common_mop_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
self, self,
new_common_mop_ty: CommonMOp<Self::PrefixPad, NewDestReg, NewSrcRegWidth, Self::SrcCount, Self::Imm>, new_common_mop_ty: CommonMOp<Self::PrefixPad, NewDestReg, NewSrcRegWidth, Self::SrcCount, Self::Imm>,
@ -636,6 +812,26 @@ macro_rules! common_mop_struct {
$($field: input.$field,)* $($field: input.$field,)*
} }
} }
#[hdl]
fn with_common_mop_sim<NewDestReg: Type, NewSrcRegWidth: Size>(
input: impl ToSimValue<Type = Self>,
new_common_mop: impl ToSimValue<
Type = CommonMOp<
Self::PrefixPad,
NewDestReg,
NewSrcRegWidth,
Self::SrcCount,
Self::Imm,
>,
>,
) -> SimValue<Self::Mapped<NewDestReg, NewSrcRegWidth>> {
let input = SimValue::into_value(input.into_sim_value());
#[hdl(sim)]
Self::Mapped::<NewDestReg, NewSrcRegWidth> {
$common: CommonMOpTrait::with_common_mop_sim(input.$common, new_common_mop),
$($field: input.$field,)*
}
}
} }
}; };
} }
@ -741,6 +937,37 @@ macro_rules! mop_enum {
} }
dest_reg dest_reg
} }
#[hdl]
fn dest_reg_sim(input: impl ToSimValue<Type = Self>) -> SimValue<Self::DestReg> {
#![allow(unreachable_patterns)]
let input = input.into_sim_value();
#[hdl(sim)]
match input {
Self::$FirstVariant(v) => <$first_ty as MOpTrait>::dest_reg_sim(v),
$(Self::$Variant(v) => <$ty as MOpTrait>::dest_reg_sim(v),)*
_ => unreachable!(),
}
}
#[hdl]
fn dest_reg_sim_ref(input: &SimValue<Self>) -> &SimValue<Self::DestReg> {
#![allow(unreachable_patterns)]
#[hdl(sim)]
match input {
Self::$FirstVariant(v) => <$first_ty as MOpTrait>::dest_reg_sim_ref(v),
$(Self::$Variant(v) => <$ty as MOpTrait>::dest_reg_sim_ref(v),)*
_ => unreachable!(),
}
}
#[hdl]
fn dest_reg_sim_mut(input: &mut SimValue<Self>) -> &mut SimValue<Self::DestReg> {
#![allow(unreachable_patterns)]
#[hdl(sim)]
match input {
Self::$FirstVariant(v) => <$first_ty as MOpTrait>::dest_reg_sim_mut(v),
$(Self::$Variant(v) => <$ty as MOpTrait>::dest_reg_sim_mut(v),)*
_ => unreachable!(),
}
}
fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType { fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType {
self.$FirstVariant.src_reg_width() self.$FirstVariant.src_reg_width()
} }
@ -755,6 +982,45 @@ macro_rules! mop_enum {
$(Self::$Variant(v) => MOpTrait::for_each_src_reg(v, f),)* $(Self::$Variant(v) => MOpTrait::for_each_src_reg(v, f),)*
} }
} }
#[hdl]
fn for_each_src_reg_sim(
input: SimValue<Self>,
f: &mut impl FnMut(SimValue<UIntType<Self::SrcRegWidth>>, usize),
) {
#![allow(unreachable_patterns)]
#[hdl(sim)]
match input {
Self::$FirstVariant(v) => MOpTrait::for_each_src_reg_sim(v, f),
$(Self::$Variant(v) => MOpTrait::for_each_src_reg_sim(v, f),)*
_ => unreachable!(),
}
}
#[hdl]
fn for_each_src_reg_sim_ref(
input: &SimValue<Self>,
f: &mut impl FnMut(&SimValue<UIntType<Self::SrcRegWidth>>, usize),
) {
#![allow(unreachable_patterns)]
#[hdl(sim)]
match input {
Self::$FirstVariant(v) => MOpTrait::for_each_src_reg_sim_ref(v, f),
$(Self::$Variant(v) => MOpTrait::for_each_src_reg_sim_ref(v, f),)*
_ => unreachable!(),
}
}
#[hdl]
fn for_each_src_reg_sim_mut(
input: &mut SimValue<Self>,
f: &mut impl FnMut(&mut SimValue<UIntType<Self::SrcRegWidth>>, usize),
) {
#![allow(unreachable_patterns)]
#[hdl(sim)]
match input {
Self::$FirstVariant(v) => MOpTrait::for_each_src_reg_sim_mut(v, f),
$(Self::$Variant(v) => MOpTrait::for_each_src_reg_sim_mut(v, f),)*
_ => unreachable!(),
}
}
fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>( fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>(
self, self,
new_dest_reg: NewDestReg, new_dest_reg: NewDestReg,
@ -784,6 +1050,33 @@ macro_rules! mop_enum {
} }
mapped_regs mapped_regs
} }
#[hdl]
fn map_regs_sim<NewDestReg: Type, NewSrcRegWidth: Size>(
input: impl ToSimValue<Type = Self>,
new_dest: impl ToSimValue<Type = NewDestReg>,
new_src_reg_width: NewSrcRegWidth::SizeType,
map_src: &mut impl FnMut(
SimValue<UIntType<Self::SrcRegWidth>>,
usize,
) -> SimValue<UIntType<NewSrcRegWidth>>,
) -> SimValue<Self::Mapped<NewDestReg, NewSrcRegWidth>> {
#![allow(unreachable_patterns)]
let input = input.into_sim_value();
let new_dest = new_dest.into_sim_value();
let mapped_ty = input.ty().mapped_ty(new_dest.ty(), new_src_reg_width);
#[hdl(sim)]
match input {
Self::$FirstVariant(v) => {
#[hdl(sim)]
mapped_ty.$FirstVariant(MOpTrait::map_regs_sim(v, new_dest, new_src_reg_width, map_src))
}
$(Self::$Variant(v) => {
#[hdl(sim)]
mapped_ty.$Variant(MOpTrait::map_regs_sim(v, new_dest, new_src_reg_width, map_src))
})*
_ => unreachable!(),
}
}
} }
}; };
( (
@ -2595,34 +2888,53 @@ impl<DestReg: Type, SrcRegWidth: Size> MoveRegMOp<DestReg, SrcRegWidth> {
} }
} }
#[hdl(cmp_eq)] #[hdl(cmp_eq, no_static)]
/// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind. /// 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 /// zero is used for built-in constants, such as the zero register
pub struct UnitNum<Width: Size> { pub struct UnitNum<C: PhantomConstGet<CpuConfig>> {
pub adj_value: UIntType<Width>, pub adj_value: UIntType<CpuConfigUnitNumWidth<C>>,
pub config: C,
} }
impl<Width: Size> UnitNum<Width> { impl<C: PhantomConstCpuConfig> UnitNum<C> {
#[hdl]
pub fn const_zero(self) -> Expr<Self> { pub fn const_zero(self) -> Expr<Self> {
self.const_zero_sim().to_expr()
}
#[hdl] #[hdl]
UnitNum { pub fn const_zero_sim(self) -> SimValue<Self> {
#[hdl(sim)]
UnitNum::<_> {
adj_value: CONST_ZERO_UNIT_NUM.cast_to(self.adj_value), adj_value: CONST_ZERO_UNIT_NUM.cast_to(self.adj_value),
config: self.config,
} }
} }
#[hdl]
pub fn from_index(self, index: usize) -> Expr<Self> { pub fn from_index(self, index: usize) -> Expr<Self> {
self.from_index_sim(index).to_expr()
}
#[hdl] #[hdl]
UnitNum { pub fn from_index_sim(self, index: usize) -> SimValue<Self> {
#[hdl(sim)]
UnitNum::<_> {
adj_value: (index + 1).cast_to(self.adj_value), adj_value: (index + 1).cast_to(self.adj_value),
config: self.config,
} }
} }
pub fn is_index(expr: impl ToExpr<Type = Self>, index: usize) -> Expr<Bool> { pub fn is_index(expr: impl ToExpr<Type = Self>, index: usize) -> Expr<Bool> {
let expr = expr.to_expr(); let expr = expr.to_expr();
expr.ty().from_index(index).adj_value.cmp_eq(expr.adj_value) expr.ty().from_index(index).adj_value.cmp_eq(expr.adj_value)
} }
pub fn index_sim(expr: &SimValue<Self>) -> Option<usize> {
let adj_value = expr.adj_value.cast_to_static::<UInt<32>>().as_int();
if adj_value == 0 {
None
} else {
Some(adj_value as usize - 1)
}
}
#[hdl] #[hdl]
pub fn as_index(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<UIntType<Width>>> { pub fn as_index(
expr: impl ToExpr<Type = Self>,
) -> Expr<HdlOption<UIntType<CpuConfigUnitNumWidth<C>>>> {
let expr = expr.to_expr(); let expr = expr.to_expr();
#[hdl] #[hdl]
let unit_index = wire(HdlOption[expr.ty().adj_value]); let unit_index = wire(HdlOption[expr.ty().adj_value]);
@ -2636,23 +2948,62 @@ impl<Width: Size> UnitNum<Width> {
} }
unit_index unit_index
} }
#[hdl]
pub fn debug_sim(this: &SimValue<Self>) -> impl fmt::Debug {
fmt::from_fn(move |f| {
#[hdl(sim)]
let Self {
adj_value,
config: _,
} = this;
f.debug_struct("UnitNum")
.field("adj_value", adj_value)
.field("unit_index", &Self::index_sim(this))
.finish_non_exhaustive()
})
}
} }
pub const CONST_ZERO_UNIT_NUM: usize = 0; pub const CONST_ZERO_UNIT_NUM: usize = 0;
#[hdl(cmp_eq)] #[hdl(cmp_eq, no_static)]
pub struct UnitOutRegNum<Width: Size> { pub struct UnitOutRegNum<C: PhantomConstGet<CpuConfig>> {
pub value: UIntType<Width>, pub value: UIntType<CpuConfigOutRegNumWidth<C>>,
pub config: C,
} }
#[hdl(cmp_eq)] impl<C: PhantomConstCpuConfig> UnitOutRegNum<C> {
#[hdl]
pub fn new_sim(self, value: usize) -> SimValue<Self> {
#[hdl(sim)]
Self {
value: value.cast_to(self.value),
config: self.config,
}
}
pub fn value_sim(this: &SimValue<Self>) -> usize {
this.value.cast_to_static::<UInt<64>>().as_int() as usize
}
#[hdl]
pub fn debug_sim(this: &SimValue<Self>) -> impl fmt::Debug {
fmt::from_fn(move |f| {
#[hdl(sim)]
let Self { value, config: _ } = this;
f.debug_struct("UnitOutRegNum")
.field("value", value)
.finish_non_exhaustive()
})
}
}
#[hdl(cmp_eq, no_static)]
/// Physical Register Number -- registers in the CPU's backend /// Physical Register Number -- registers in the CPU's backend
pub struct PRegNum<UnitNumWidth: Size, OutRegNumWidth: Size> { pub struct PRegNum<C: PhantomConstGet<CpuConfig>> {
pub unit_num: UnitNum<UnitNumWidth>, pub unit_num: UnitNum<C>,
pub unit_out_reg: UnitOutRegNum<OutRegNumWidth>, pub unit_out_reg: UnitOutRegNum<C>,
} }
impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWidth> { impl<C: PhantomConstCpuConfig> PRegNum<C> {
#[hdl] #[hdl]
pub fn const_zero(self) -> Expr<Self> { pub fn const_zero(self) -> Expr<Self> {
#[hdl] #[hdl]
@ -2661,9 +3012,24 @@ impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWi
unit_out_reg: #[hdl] unit_out_reg: #[hdl]
UnitOutRegNum { UnitOutRegNum {
value: 0u8.cast_to(self.unit_out_reg.value), value: 0u8.cast_to(self.unit_out_reg.value),
config: self.unit_out_reg.config,
}, },
} }
} }
#[hdl]
pub fn debug_sim(this: &SimValue<Self>) -> impl fmt::Debug {
fmt::from_fn(move |f| {
#[hdl(sim)]
let Self {
unit_num,
unit_out_reg,
} = this;
f.debug_struct("PRegNum")
.field("unit_num", &UnitNum::debug_sim(unit_num))
.field("unit_out_reg", &UnitOutRegNum::debug_sim(unit_out_reg))
.finish()
})
}
} }
#[hdl(cmp_eq)] #[hdl(cmp_eq)]
@ -2949,6 +3315,29 @@ impl MOpDestReg {
} }
}) })
} }
#[hdl]
pub fn regs_sim(this: &SimValue<Self>) -> [u32; Self::REG_COUNT] {
let this = this.into_sim_value();
std::array::from_fn(|index| match Self::REG_KINDS[index] {
MOpDestRegKind::NormalReg { dest_reg_index } => this.normal_regs[dest_reg_index]
.value
.cast_to_static::<UInt<32>>()
.as_int(),
MOpDestRegKind::FlagReg {
flag_reg_index,
reg_num,
} =>
{
#[hdl(sim)]
if let HdlSome(v) = &this.flag_regs[flag_reg_index] {
let () = **v;
reg_num
} else {
MOpRegNum::CONST_ZERO_REG_NUM
}
}
})
}
} }
#[hdl] #[hdl]

View file

@ -192,6 +192,13 @@ impl MOpRegNum {
power_isa_gpr_or_zero_reg power_isa_gpr_or_zero_reg
} }
#[hdl] #[hdl]
pub fn power_isa_gpr_or_zero_reg_imm(index: usize) -> Expr<Self> {
#[hdl]
Self {
value: Self::power_isa_gpr_or_zero_reg_num(index).cast_to_static::<UInt<_>>(),
}
}
#[hdl]
pub fn power_isa_gpr_or_zero_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> { pub fn power_isa_gpr_or_zero_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> {
#[hdl(sim)] #[hdl(sim)]
Self { Self {

View file

@ -7,7 +7,9 @@ pub mod instruction;
pub mod main_memory_and_io; pub mod main_memory_and_io;
pub mod next_pc; pub mod next_pc;
pub mod powerisa_instructions_xml; pub mod powerisa_instructions_xml;
#[cfg(todo)]
pub mod reg_alloc; pub mod reg_alloc;
pub mod register; pub mod register;
pub mod rename_execute_retire;
pub mod unit; pub mod unit;
pub mod util; pub mod util;

View file

@ -0,0 +1,920 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{
CpuConfig, CpuConfig2PowOutRegNumWidth, CpuConfigFetchWidth, CpuConfigMaxUnitMaxInFlight,
CpuConfigPRegNumWidth, CpuConfigRobSize, CpuConfigUnitCount, PhantomConstCpuConfig,
TwiceCpuConfigFetchWidth,
},
instruction::{MOp, MOpDestReg, MOpRegNum, MOpTrait, PRegNum, UnitNum, UnitOutRegNum},
next_pc::{CallStackOp, SimValueDefault},
unit::{UnitKind, UnitMOp},
util::array_vec::ArrayVec,
};
use fayalite::{
int::UIntInRangeInclusiveType,
prelude::*,
ty::{OpaqueSimValue, StaticType},
util::ready_valid::ReadyValid,
};
use std::{collections::VecDeque, fmt};
pub const MOP_ID_WIDTH: usize = 16;
#[hdl]
pub type MOpId = UInt<{ MOP_ID_WIDTH }>;
#[hdl]
/// A &micro;Op along with the state needed for this instance of the &micro;Op.
pub struct MOpInstance<MOp> {
pub fetch_block_id: UInt<8>,
pub id: MOpId,
pub pc: UInt<64>,
/// initialized to 0 by decoder, overwritten by `next_pc()`
pub predicted_next_pc: UInt<64>,
pub size_in_bytes: UInt<4>,
/// `true` if this &micro;Op is the first &micro;Op in the ISA-level instruction.
/// In general, a single &micro;Op can't be cancelled by itself,
/// it needs to be cancelled along with all other &micro;Ops that
/// come from the same ISA-level instruction.
pub is_first_mop_in_insn: Bool,
pub mop: MOp,
}
#[hdl(no_static)]
/// TODO: merge with [`crate::next_pc::PostDecodeOutputInterface`]
pub struct PostDecodeOutputInterface<C: PhantomConstGet<CpuConfig>> {
pub insns: ArrayVec<MOpInstance<MOp>, CpuConfigFetchWidth<C>>,
#[hdl(flip)]
pub ready: UIntInRangeInclusiveType<ConstUsize<0>, CpuConfigFetchWidth<C>>,
/// tells the rename/execute/retire circuit to cancel all non-retired instructions
pub cancel: ReadyValid<()>,
pub config: C,
}
#[hdl(no_static)]
pub struct NextPcPredictorOp<C: PhantomConstGet<CpuConfig>> {
pub call_stack_op: CallStackOp,
/// should be `HdlSome(taken)` for any conditional control-flow instruction
/// with an immediate target that can be predicted as taken/not-taken (branch/call/return).
pub cond_br_taken: HdlOption<Bool>,
pub config: C,
}
#[hdl(no_static)]
/// TODO: merge with [`crate::next_pc::RetireToNextPcInterfaceInner`]
pub enum RetireToNextPcInterfaceInner<C: PhantomConstGet<CpuConfig>> {
CancelAndStartAt(UInt<64>),
RetiredInstructions(ArrayVec<NextPcPredictorOp<C>, CpuConfigFetchWidth<C>>),
}
#[hdl(no_static)]
/// handles updating speculative branch predictor state (e.g. branch histories)
/// when instructions retire, as well as updating state when a
/// branch instruction is mis-speculated.
pub struct RetireToNextPcInterface<C: PhantomConstGet<CpuConfig>> {
pub inner: ReadyValid<RetireToNextPcInterfaceInner<C>>,
/// only for debugging
pub next_insns: HdlOption<ArrayVec<MOpInstance<MOp>, CpuConfigRobSize<C>>>,
}
#[hdl]
pub type RenamedMOp<C: PhantomConstGet<CpuConfig>> =
crate::instruction::RenamedMOp<PRegNum<C>, CpuConfigPRegNumWidth<C>>;
fn zeroed<T: Type>(ty: T) -> SimValue<T> {
SimValue::from_opaque(
ty,
OpaqueSimValue::from_bits(UInt::new(ty.canonical().bit_width()).zero()),
)
}
impl<C: PhantomConstCpuConfig> SimValueDefault for RenameExecuteRetireDebugState<C> {
fn sim_value_default(self) -> SimValue<Self> {
zeroed(self)
}
}
#[hdl(no_static)]
enum RenameTableEntry<C: PhantomConstGet<CpuConfig>> {
L1(PRegNum<C>),
L2(UInt<{ MOpRegNum::WIDTH }>),
}
impl<C: PhantomConstCpuConfig> RenameTableEntry<C> {
#[hdl]
fn const_zero(self) -> SimValue<Self> {
#[hdl(sim)]
self.L1(self.L1.const_zero())
}
#[hdl]
fn debug_sim(this: &SimValue<Self>) -> impl fmt::Debug {
fmt::from_fn(move |f| {
#[hdl(sim)]
match this {
Self::L1(v) => write!(f, "L1({:?})", PRegNum::debug_sim(v)),
Self::L2(v) => write!(f, "L2({v:?})"),
}
})
}
}
/// make arrays dynamically-sized to avoid putting large types on the stack
#[hdl(get(|c| 1 << MOpRegNum::WIDTH))]
type MOpRegCount<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(no_static)]
struct RenameTableDebugState<C: PhantomConstGet<CpuConfig>> {
entries: ArrayType<RenameTableEntry<C>, MOpRegCount<C>>,
unit_out_reg_ref_counts: ArrayType<
ArrayType<
UIntInRangeInclusiveType<ConstUsize<0>, MOpRegCount<C>>,
CpuConfig2PowOutRegNumWidth<C>,
>,
CpuConfigUnitCount<C>,
>,
l2_reg_ref_counts:
ArrayType<UIntInRangeInclusiveType<ConstUsize<0>, MOpRegCount<C>>, MOpRegCount<C>>,
config: C,
}
#[derive(Debug)]
struct RenameTable<C: PhantomConstCpuConfig> {
entries: Box<[SimValue<RenameTableEntry<C>>; 1 << MOpRegNum::WIDTH]>,
unit_out_reg_ref_counts: Box<[Box<[usize]>]>,
l2_reg_ref_counts: Box<[usize; 1 << MOpRegNum::WIDTH]>,
config: C,
}
impl<C: PhantomConstCpuConfig> Clone for RenameTable<C> {
fn clone(&self) -> Self {
Self {
entries: self.entries.clone(),
unit_out_reg_ref_counts: self.unit_out_reg_ref_counts.clone(),
l2_reg_ref_counts: self.l2_reg_ref_counts.clone(),
config: self.config.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
let Self {
entries,
unit_out_reg_ref_counts,
l2_reg_ref_counts,
config,
} = self;
entries.clone_from(&source.entries);
unit_out_reg_ref_counts.clone_from(&source.unit_out_reg_ref_counts);
l2_reg_ref_counts.clone_from(&source.l2_reg_ref_counts);
*config = source.config;
}
}
impl<C: PhantomConstCpuConfig> RenameTable<C> {
fn new(config: C) -> Self {
let entries: Box<[SimValue<RenameTableEntry<C>>; 1 << MOpRegNum::WIDTH]> =
vec![RenameTableEntry[config].const_zero(); 1 << MOpRegNum::WIDTH]
.try_into()
.expect("size is known to match");
let for_unit = vec![0; CpuConfig2PowOutRegNumWidth[config]].into_boxed_slice();
let unit_out_reg_ref_counts = vec![for_unit; CpuConfigUnitCount[config]].into_boxed_slice();
Self {
entries,
unit_out_reg_ref_counts,
l2_reg_ref_counts: vec![0; 1 << MOpRegNum::WIDTH]
.try_into()
.expect("size is known to match"),
config,
}
}
#[hdl]
fn to_debug_state(&self) -> SimValue<RenameTableDebugState<C>> {
let Self {
entries,
unit_out_reg_ref_counts,
l2_reg_ref_counts,
config,
} = self;
let ty = RenameTableDebugState[*config];
#[hdl(sim)]
RenameTableDebugState::<_> {
entries: entries.to_sim_value_with_type(ty.entries),
unit_out_reg_ref_counts: unit_out_reg_ref_counts
.to_sim_value_with_type(ty.unit_out_reg_ref_counts),
l2_reg_ref_counts: l2_reg_ref_counts.to_sim_value_with_type(ty.l2_reg_ref_counts),
config,
}
}
fn find_free_unit_out_reg(&self, unit_index: usize) -> Option<usize> {
self.unit_out_reg_ref_counts[unit_index]
.iter()
.position(|v| *v == 0)
}
fn inc_unit_out_reg_ref_count(&mut self, unit_index: usize, reg_index: usize) {
self.unit_out_reg_ref_counts[unit_index][reg_index] += 1;
}
fn dec_unit_out_reg_ref_count(&mut self, unit_index: usize, reg_index: usize) {
self.unit_out_reg_ref_counts[unit_index][reg_index] -= 1;
}
fn inc_l2_reg_ref_count(&mut self, l2_reg_index: usize) {
self.l2_reg_ref_counts[l2_reg_index] += 1;
}
fn dec_l2_reg_ref_count(&mut self, l2_reg_index: usize) {
self.l2_reg_ref_counts[l2_reg_index] -= 1;
}
#[hdl]
fn write_rename_table(&mut self, unrenamed_reg_num: u32, new: SimValue<RenameTableEntry<C>>) {
if unrenamed_reg_num == MOpRegNum::CONST_ZERO_REG_NUM {
// writing to const zero reg does nothing
return;
}
println!(
"write_rename_table({unrenamed_reg_num:#x}, {:?})",
RenameTableEntry::debug_sim(&new),
);
#[hdl(sim)]
match &self.entries[unrenamed_reg_num as usize] {
RenameTableEntry::<_>::L1(entry) => {
if let Some(unit_index) = UnitNum::index_sim(&entry.unit_num) {
self.dec_unit_out_reg_ref_count(
unit_index,
UnitOutRegNum::value_sim(&entry.unit_out_reg),
);
}
}
RenameTableEntry::<_>::L2(entry) => {
self.dec_l2_reg_ref_count(entry.cast_to_static::<UInt<64>>().as_int() as usize);
}
}
self.entries[unrenamed_reg_num as usize] = new;
#[hdl(sim)]
match &self.entries[unrenamed_reg_num as usize] {
RenameTableEntry::<_>::L1(entry) => {
if let Some(unit_index) = UnitNum::index_sim(&entry.unit_num) {
self.inc_unit_out_reg_ref_count(
unit_index,
UnitOutRegNum::value_sim(&entry.unit_out_reg),
);
}
}
RenameTableEntry::<_>::L2(entry) => {
self.inc_l2_reg_ref_count(entry.cast_to_static::<UInt<64>>().as_int() as usize);
}
}
}
}
#[hdl(no_static)]
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
mop: MOpInstance<RenamedMOp<C>>,
unit_num: UnitNum<C>,
config: C,
}
impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
fn sim_value_default(self) -> SimValue<Self> {
zeroed(self)
}
}
#[derive(Debug)]
/// owns a ref count on the dest reg
struct RobEntry<C: PhantomConstCpuConfig> {
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
unit_index: usize,
config: C,
}
#[hdl]
struct OrigMOpQueueEntryDebugState {
mop: MOpInstance<MOp>,
/// number of renamed &micro;Ops that this non-renamed &micro;Op corresponds to
renamed_mop_count: UInt<8>,
}
#[derive(Debug)]
struct OrigMOpQueueEntry {
mop: SimValue<MOpInstance<MOp>>,
/// number of renamed &micro;Ops that this non-renamed &micro;Op corresponds to
renamed_mop_count: u8,
}
#[hdl(no_static)]
struct UnitDebugState<C: PhantomConstGet<CpuConfig>> {
assigned_rob_entries: ArrayVec<MOpId, CpuConfigMaxUnitMaxInFlight<C>>,
/// see [`UnitState::started_l2_store`]
started_l2_store: Bool,
config: C,
}
#[derive(Debug)]
struct UnitState<C: PhantomConstCpuConfig> {
assigned_rob_entries: VecDeque<SimValue<MOpId>>,
/// `true` if a L2 register file write was started for this unit after the last &micro;Op was
/// assigned to this unit.
/// So, if this unit runs out of registers and a L2 register file write is started, this gets
/// set to `true`, and if a new &micro;Op is assigned to this unit, this gets set to `false`.
started_l2_store: bool,
config: C,
}
impl<C: PhantomConstCpuConfig> UnitState<C> {
fn finish_cancel(&mut self) {
let Self {
assigned_rob_entries,
started_l2_store,
config: _,
} = self;
assigned_rob_entries.clear();
*started_l2_store = false;
}
}
#[hdl]
enum CancelingDebugState {
NeedSendCancel(UInt<64>),
NeedReceiveCancel,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum CancelingState {
NeedSendCancel(u64),
NeedReceiveCancel,
}
#[hdl(no_static)]
pub struct RenameExecuteRetireDebugState<C: PhantomConstGet<CpuConfig>> {
next_mop_id: MOpId,
rename_delayed: ArrayVec<MOpInstance<MOp>, TwiceCpuConfigFetchWidth<C>>,
next_renamed_mop_count: UInt<8>,
rename_table: RenameTableDebugState<C>,
retire_rename_table: RenameTableDebugState<C>,
rob: ArrayVec<RobEntryDebugState<C>, CpuConfigRobSize<C>>,
orig_mop_queue: ArrayVec<OrigMOpQueueEntryDebugState, CpuConfigRobSize<C>>,
units: ArrayType<UnitDebugState<C>, CpuConfigUnitCount<C>>,
canceling: HdlOption<CancelingDebugState>,
}
#[derive(Debug)]
struct RenameExecuteRetireState<C: PhantomConstCpuConfig> {
next_mop_id: SimValue<MOpId>,
rename_delayed: VecDeque<SimValue<MOpInstance<MOp>>>,
/// count of renamed &micro;Ops that have been started that correspond to the next un-renamed &micro;Op in `rename_delayed`
next_renamed_mop_count: u8,
rename_table: RenameTable<C>,
retire_rename_table: RenameTable<C>,
rob: VecDeque<RobEntry<C>>,
orig_mop_queue: VecDeque<OrigMOpQueueEntry>,
units: Box<[UnitState<C>]>,
canceling: Option<CancelingState>,
l2_reg_file_unit_index: usize,
config: C,
}
impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
fn new(config: C) -> Self {
let rename_table = RenameTable::new(config);
Self {
next_mop_id: MOpId.zero().into_sim_value(),
rename_delayed: VecDeque::with_capacity(TwiceCpuConfigFetchWidth[config]),
next_renamed_mop_count: 0,
rename_table: rename_table.clone(),
retire_rename_table: rename_table,
rob: VecDeque::with_capacity(CpuConfigRobSize[config]),
orig_mop_queue: VecDeque::with_capacity(CpuConfigRobSize[config]),
units: Box::from_iter((0..config.get().units.len()).map(|unit_index| UnitState {
assigned_rob_entries: VecDeque::with_capacity(
config.get().unit_max_in_flight(unit_index).get(),
),
started_l2_store: false,
config,
})),
canceling: None,
l2_reg_file_unit_index: config
.get()
.units
.iter()
.position(|unit| unit.kind == UnitKind::TransformedMove)
.expect("Unit for L2 register file is missing"),
config,
}
}
#[hdl]
async fn write_for_debug(
&self,
sim: &mut ExternModuleSimulationState,
state_for_debug: Expr<RenameExecuteRetireDebugState<C>>,
) {
let Self {
ref next_mop_id,
ref rename_delayed,
next_renamed_mop_count,
ref rename_table,
ref retire_rename_table,
ref rob,
ref orig_mop_queue,
ref units,
ref canceling,
l2_reg_file_unit_index: _,
config,
} = *self;
sim.write(
state_for_debug,
#[hdl(sim)]
RenameExecuteRetireDebugState::<_> {
next_mop_id,
rename_delayed: state_for_debug
.ty()
.rename_delayed
.from_iter_sim(zeroed(StaticType::TYPE), rename_delayed)
.expect("known to fit"),
next_renamed_mop_count,
rename_table: rename_table.to_debug_state(),
retire_rename_table: retire_rename_table.to_debug_state(),
rob: state_for_debug
.ty()
.rob
.from_iter_sim(
zeroed(RobEntryDebugState[config]),
rob.iter().map(|entry| {
let RobEntry {
mop,
unit_index,
config: _,
} = entry;
#[hdl(sim)]
RobEntryDebugState::<_> {
mop,
unit_num: UnitNum[config].from_index_sim(*unit_index),
config,
}
}),
)
.expect("known to fit"),
orig_mop_queue: state_for_debug
.ty()
.orig_mop_queue
.from_iter_sim(
zeroed(OrigMOpQueueEntryDebugState),
orig_mop_queue.iter().map(|entry| {
let OrigMOpQueueEntry {
mop,
renamed_mop_count,
} = entry;
#[hdl(sim)]
OrigMOpQueueEntryDebugState {
mop,
renamed_mop_count,
}
}),
)
.expect("known to fit"),
units: SimValue::from_array_elements(
state_for_debug.ty().units,
units.iter().map(|unit| {
let UnitState {
assigned_rob_entries,
started_l2_store,
config: _,
} = unit;
let ty = UnitDebugState[config];
#[hdl(sim)]
UnitDebugState::<_> {
assigned_rob_entries: ty
.assigned_rob_entries
.from_iter_sim(zeroed(UInt::new_static()), assigned_rob_entries)
.expect("known to fit"),
started_l2_store,
config,
}
}),
),
canceling: match canceling {
Some(canceling) =>
{
#[hdl(sim)]
HdlSome(match canceling {
CancelingState::NeedSendCancel(v) =>
{
#[hdl(sim)]
CancelingDebugState.NeedSendCancel(v)
}
CancelingState::NeedReceiveCancel =>
{
#[hdl(sim)]
CancelingDebugState.NeedReceiveCancel()
}
})
}
None =>
{
#[hdl(sim)]
HdlNone()
}
},
},
)
.await;
}
#[hdl]
async fn write_to_next_pc_next_insns(
&self,
sim: &mut ExternModuleSimulationState,
next_insns: Expr<HdlOption<ArrayVec<MOpInstance<MOp>, CpuConfigRobSize<C>>>>,
) {
sim.write(
next_insns,
if self.canceling.is_some() {
#[hdl(sim)]
(next_insns.ty()).HdlNone()
} else {
#[hdl(sim)]
(next_insns.ty()).HdlSome(
next_insns
.ty()
.HdlSome
.from_iter_sim(
zeroed(MOpInstance[MOp]),
self.rename_delayed
.iter()
.chain(self.orig_mop_queue.iter().map(|entry| &entry.mop)),
)
.expect("known to fit"),
)
},
)
.await;
}
fn space_available_for_unit(&self, unit_index: usize) -> usize {
self.config
.get()
.unit_max_in_flight(unit_index)
.get()
.saturating_sub(self.units[unit_index].assigned_rob_entries.len())
}
#[hdl]
fn try_rename(
&mut self,
insn: SimValue<MOpInstance<MOp>>,
) -> Result<(), SimValue<MOpInstance<MOp>>> {
let unit_kind = UnitMOp::kind_sim(&insn.mop);
if let UnitKind::TransformedMove = unit_kind {
todo!("handle reg-reg moves in rename stage");
}
#[derive(Clone, Copy)]
struct ChosenUnit {
unit_index: usize,
out_reg_num: Option<usize>,
space_available: usize,
}
impl ChosenUnit {
fn is_better_than(self, other: Self) -> bool {
let Self {
unit_index: _,
out_reg_num,
space_available,
} = self;
if out_reg_num.is_some() != other.out_reg_num.is_some() {
out_reg_num.is_some()
} else {
space_available > other.space_available
}
}
}
let mut chosen_unit = None;
for (unit_index, unit_state) in self.units.iter().enumerate() {
if self.config.get().units[unit_index].kind != unit_kind {
continue;
}
let cur_unit = ChosenUnit {
unit_index,
out_reg_num: self.rename_table.find_free_unit_out_reg(unit_index),
space_available: self.space_available_for_unit(unit_index),
};
let chosen_unit = chosen_unit.get_or_insert(cur_unit);
if cur_unit.is_better_than(*chosen_unit) {
*chosen_unit = cur_unit;
}
}
let Some(ChosenUnit {
unit_index,
out_reg_num,
space_available,
}) = chosen_unit
else {
panic!(
"there are no units of kind: {unit_kind:?}:\n{:?}",
self.config,
);
};
if space_available == 0 {
return Err(insn);
}
let Some(out_reg_num) = out_reg_num else {
if self.units[unit_index].started_l2_store {
if self.space_available_for_unit(self.l2_reg_file_unit_index) > 0 {
todo!("start a L2 register file store");
}
}
return Err(insn);
};
if self.rob.len() >= self.config.get().rob_size.get() {
return Err(insn);
};
let out_reg_num_sim = UnitOutRegNum[self.config].new_sim(out_reg_num);
#[hdl(sim)]
let MOpInstance::<_> {
fetch_block_id,
id: _,
pc,
predicted_next_pc,
size_in_bytes,
is_first_mop_in_insn,
mop,
} = &insn;
let mut needed_load = None;
let unrenamed_dest_regs = MOpDestReg::regs_sim(MOpTrait::dest_reg_sim_ref(mop));
let renamed_dest_reg = #[hdl(sim)]
PRegNum::<_> {
unit_num: UnitNum[self.config].from_index_sim(unit_index),
unit_out_reg: out_reg_num_sim,
};
let mop = MOpTrait::map_regs_sim(
mop,
&renamed_dest_reg,
CpuConfigPRegNumWidth[self.config],
&mut |src_reg, index| {
let renamed = &self.rename_table.entries[src_reg.as_int() as usize];
println!(
"renaming src[{index}] from {src_reg:?} to {:?}",
RenameTableEntry::debug_sim(renamed),
);
#[hdl(sim)]
match renamed {
RenameTableEntry::<_>::L1(v) => v.cast_to_bits(),
RenameTableEntry::<_>::L2(v) => {
needed_load.get_or_insert_with(|| v.clone());
PRegNum[self.config]
.const_zero()
.cast_to_bits()
.into_sim_value()
}
}
},
);
if let Some(needed_load) = needed_load {
todo!("start a read from L2 register file instead: {needed_load:?}");
}
let mop = UnitMOp::with_transformed_move_op_sim(
mop,
RenamedMOp[self.config].TransformedMove,
|_move_reg| unreachable!(),
);
// inc ref count owned by the new RobEntry
self.rename_table
.inc_unit_out_reg_ref_count(unit_index, out_reg_num);
let renamed_dest_reg = #[hdl(sim)]
(RenameTableEntry[self.config]).L1(renamed_dest_reg);
for unrenamed_dest_reg in unrenamed_dest_regs {
self.rename_table
.write_rename_table(unrenamed_dest_reg, renamed_dest_reg.clone());
}
self.orig_mop_queue.push_back(OrigMOpQueueEntry {
mop: insn.clone(),
renamed_mop_count: 1 + self.next_renamed_mop_count,
});
self.next_renamed_mop_count = 0;
self.rob.push_back(RobEntry {
mop: #[hdl(sim)]
MOpInstance::<_> {
fetch_block_id,
id: &self.next_mop_id,
pc,
predicted_next_pc,
size_in_bytes,
is_first_mop_in_insn,
mop,
},
unit_index,
config: self.config,
});
self.units[unit_index]
.assigned_rob_entries
.push_back(self.next_mop_id.clone());
self.next_mop_id = self.next_mop_id.as_int().wrapping_add(1).into_sim_value();
Ok(())
}
fn get_from_post_decode_ready(&self) -> usize {
if self.canceling.is_some() {
0
} else {
TwiceCpuConfigFetchWidth[self.config]
.saturating_sub(self.rename_delayed.len())
.min(CpuConfigFetchWidth[self.config])
}
}
fn handle_from_post_decode(&mut self, insns: &[SimValue<MOpInstance<MOp>>]) {
if insns.is_empty() {
return;
}
assert!(self.canceling.is_none());
for insn in insns {
self.rename_delayed.push_back(insn.clone());
}
for _ in 0..CpuConfigFetchWidth[self.config] {
let Some(insn) = self.rename_delayed.pop_front() else {
break;
};
match self.try_rename(insn) {
Ok(()) => {}
Err(insn) => {
self.rename_delayed.push_front(insn);
break;
}
}
}
}
#[hdl]
fn finish_receive_cancel(&mut self) {
let Self {
next_mop_id: _,
rename_delayed,
next_renamed_mop_count,
rename_table,
retire_rename_table,
rob,
orig_mop_queue,
units,
canceling,
l2_reg_file_unit_index: _,
config: _,
} = self;
assert_eq!(*canceling, Some(CancelingState::NeedReceiveCancel));
rename_delayed.clear();
*next_renamed_mop_count = 0;
rename_table.clone_from(retire_rename_table);
rob.clear();
orig_mop_queue.clear();
for unit in units {
unit.finish_cancel();
}
*canceling = None;
}
#[hdl]
fn finish_send_cancel(&mut self) {
assert!(matches!(
self.canceling,
Some(CancelingState::NeedSendCancel(_))
));
self.canceling = Some(CancelingState::NeedReceiveCancel);
}
#[hdl]
fn retire_peek(&self) -> SimValue<HdlOption<RetireToNextPcInterfaceInner<C>>> {
let ty = RetireToNextPcInterfaceInner[self.config];
let next_pc_predictor_op = NextPcPredictorOp[self.config];
match self.canceling {
Some(CancelingState::NeedSendCancel(v)) =>
{
#[hdl(sim)]
(HdlOption[ty]).HdlSome(
#[hdl(sim)]
ty.CancelAndStartAt(v),
)
}
Some(CancelingState::NeedReceiveCancel) =>
{
#[hdl(sim)]
(HdlOption[ty]).HdlNone()
}
None => {
let mut retired_insns = Vec::<SimValue<NextPcPredictorOp<_>>>::new();
// TODO: implement
#[hdl(sim)]
(HdlOption[ty]).HdlSome(
#[hdl(sim)]
ty.RetiredInstructions(
ty.RetiredInstructions
.from_iter_sim(zeroed(next_pc_predictor_op), retired_insns)
.expect("known to fit"),
),
)
}
}
}
fn retire_one(&mut self, retire: &SimValue<NextPcPredictorOp<C>>) {
assert!(self.canceling.is_none());
todo!("{retire:#?}");
}
}
#[hdl]
async fn rename_execute_retire_run(
mut sim: ExternModuleSimulationState,
cd: Expr<ClockDomain>,
from_post_decode: Expr<PostDecodeOutputInterface<PhantomConst<CpuConfig>>>,
to_next_pc: Expr<RetireToNextPcInterface<PhantomConst<CpuConfig>>>,
state_for_debug: Expr<RenameExecuteRetireDebugState<PhantomConst<CpuConfig>>>,
config: PhantomConst<CpuConfig>,
) {
let mut state = RenameExecuteRetireState::new(config);
loop {
state
.write_to_next_pc_next_insns(&mut sim, to_next_pc.next_insns)
.await;
state.write_for_debug(&mut sim, state_for_debug).await;
let from_post_decode_ready = state.get_from_post_decode_ready();
assert!(from_post_decode_ready <= from_post_decode.ty().ready.end());
sim.write(from_post_decode.ready, from_post_decode_ready)
.await;
sim.write(
from_post_decode.cancel.ready,
state.canceling == Some(CancelingState::NeedReceiveCancel),
)
.await;
let retire_peek = state.retire_peek();
sim.write(to_next_pc.inner.data, &retire_peek).await;
sim.wait_for_clock_edge(cd.clk).await;
let from_post_decode_insns = sim.read_past(from_post_decode.insns, cd.clk).await;
let from_post_decode_insns = ArrayVec::elements_sim_ref(&from_post_decode_insns);
state.handle_from_post_decode(
from_post_decode_insns
.get(..from_post_decode_ready)
.unwrap_or(from_post_decode_insns),
);
match state.canceling {
Some(CancelingState::NeedReceiveCancel) => {
#[hdl(sim)]
if let HdlSome(_) = sim.read_past(from_post_decode.cancel.data, cd.clk).await {
state.finish_receive_cancel();
}
}
Some(CancelingState::NeedSendCancel(_)) => {
if sim.read_past_bool(to_next_pc.inner.ready, cd.clk).await {
state.finish_send_cancel();
}
}
None => {
if sim.read_past_bool(to_next_pc.inner.ready, cd.clk).await {
let ops = #[hdl(sim)]
if let HdlSome(v) = retire_peek {
#[hdl(sim)]
if let RetireToNextPcInterfaceInner::<_>::RetiredInstructions(ops) = v {
ops
} else {
unreachable!()
}
} else {
unreachable!()
};
for op in ArrayVec::elements_sim_ref(&ops) {
state.retire_one(op);
}
}
}
}
}
}
#[hdl_module(extern)]
pub fn rename_execute_retire(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let from_post_decode: PostDecodeOutputInterface<PhantomConst<CpuConfig>> =
m.input(PostDecodeOutputInterface[config]);
#[hdl]
let to_next_pc: RetireToNextPcInterface<PhantomConst<CpuConfig>> =
m.output(RetireToNextPcInterface[config]);
#[hdl]
let state_for_debug: RenameExecuteRetireDebugState<PhantomConst<CpuConfig>> =
m.output(RenameExecuteRetireDebugState[config]);
m.register_clock_for_past(cd.clk);
m.extern_module_simulation_fn(
(cd, from_post_decode, to_next_pc, state_for_debug, config),
|(cd, from_post_decode, to_next_pc, state_for_debug, config), mut sim| async move {
sim.write(state_for_debug, state_for_debug.ty().sim_value_default())
.await;
sim.resettable(
cd,
|mut sim: ExternModuleSimulationState| async move {
sim.write(from_post_decode.ready, 0usize).await;
sim.write(from_post_decode.cancel.ready, false).await;
sim.write(to_next_pc.inner.data, to_next_pc.ty().inner.data.HdlNone())
.await;
sim.write(to_next_pc.next_insns, to_next_pc.ty().next_insns.HdlNone())
.await;
},
|sim, ()| {
rename_execute_retire_run(
sim,
cd,
from_post_decode,
to_next_pc,
state_for_debug,
config,
)
},
)
.await;
},
);
}

View file

@ -2,7 +2,7 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
config::CpuConfig, config::{CpuConfig, PhantomConstCpuConfig},
instruction::{ instruction::{
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, UnitOutRegNum, MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, UnitOutRegNum,
@ -32,7 +32,7 @@ macro_rules! all_units {
$( $(
$(#[transformed_move $($transformed_move:tt)*])? $(#[transformed_move $($transformed_move:tt)*])?
#[create_dyn_unit_fn = $create_dyn_unit_fn:expr] #[create_dyn_unit_fn = $create_dyn_unit_fn:expr]
#[extract = $extract:ident] #[extract($extract:ident, $extract_sim:ident, $extract_sim_ref:ident, $extract_sim_mut:ident)]
$(#[$variant_meta:meta])* $(#[$variant_meta:meta])*
$Unit:ident($Op:ty), $Unit:ident($Op:ty),
)* )*
@ -48,7 +48,7 @@ macro_rules! all_units {
} }
impl $UnitKind { impl $UnitKind {
pub fn unit(self, config: &CpuConfig, unit_index: usize) -> DynUnit { pub fn unit(self, config: PhantomConst<CpuConfig>, unit_index: usize) -> DynUnit {
match self { match self {
$($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)* $($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)*
} }
@ -112,6 +112,15 @@ macro_rules! all_units {
} }
unit_kind unit_kind
} }
#[hdl]
$vis fn kind_sim(expr: &SimValue<Self>) -> UnitKind {
#![allow(unreachable_patterns)]
#[hdl(sim)]
match expr {
$(Self::$Unit(_) => $UnitKind::$Unit,)*
_ => unreachable!(),
}
}
$( $(
#[hdl] #[hdl]
$vis fn $extract(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<$Op>> { $vis fn $extract(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<$Op>> {
@ -126,6 +135,34 @@ macro_rules! all_units {
} }
$extract $extract
} }
#[hdl]
$vis fn $extract_sim(expr: impl ToSimValue<Type = Self>) -> Option<SimValue<$Op>> {
let expr = expr.into_sim_value();
#[hdl(sim)]
if let Self::$Unit(v) = expr {
Some(v)
} else {
None
}
}
#[hdl]
$vis fn $extract_sim_ref(expr: &SimValue<Self>) -> Option<&SimValue<$Op>> {
#[hdl(sim)]
if let Self::$Unit(v) = expr {
Some(v)
} else {
None
}
}
#[hdl]
$vis fn $extract_sim_mut(expr: &mut SimValue<Self>) -> Option<&mut SimValue<$Op>> {
#[hdl(sim)]
if let Self::$Unit(v) = expr {
Some(v)
} else {
None
}
}
)* )*
$vis fn with_transformed_move_op_ty<T>(self, new_transformed_move_op_ty: T) -> $UnitMOpEnum<$DestReg, $SrcRegWidth, T> $vis fn with_transformed_move_op_ty<T>(self, new_transformed_move_op_ty: T) -> $UnitMOpEnum<$DestReg, $SrcRegWidth, T>
where where
@ -196,6 +233,45 @@ macro_rules! all_units {
} }
with_transformed_move_op with_transformed_move_op
} }
#[hdl]
$vis fn try_with_transformed_move_op_sim<T, E>(
this: impl ToSimValue<Type = Self>,
new_transformed_move_op_ty: T,
f: impl FnOnce(SimValue<$TransformedMoveOp>) -> Result<SimValue<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>, E>,
) -> Result<SimValue<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>, E>
where
T: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
$TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
{
#![allow(unreachable_patterns)]
let this = this.into_sim_value();
let new_ty = this.ty().with_transformed_move_op_ty(new_transformed_move_op_ty);
#[hdl(sim)]
match this {
$(Self::$BeforeUnit(unit) => Ok(
#[hdl(sim)]
new_ty.$BeforeUnit(unit)
),)*
Self::$TransformedMove(unit) => f(unit),
$(Self::$AfterUnit(unit) => Ok(
#[hdl(sim)]
new_ty.$AfterUnit(unit)
),)*
_ => unreachable!(),
}
}
$vis fn with_transformed_move_op_sim<T>(
this: impl ToSimValue<Type = Self>,
new_transformed_move_op_ty: T,
f: impl FnOnce(SimValue<$TransformedMoveOp>) -> SimValue<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>,
) -> SimValue<$UnitMOpEnum<$DestReg, $SrcRegWidth, T>>
where
T: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
$TransformedMoveOp: MOpTrait<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
{
let Ok::<_, std::convert::Infallible>(retval) = Self::try_with_transformed_move_op_sim(this, new_transformed_move_op_ty, move |v| Ok(f(v)));
retval
}
} }
const _: () = { const _: () = {
@ -254,14 +330,14 @@ all_units! {
})] TransformedMoveOp: Type })] TransformedMoveOp: Type
> { > {
#[create_dyn_unit_fn = |config, unit_index| alu_branch::AluBranch::new(config, unit_index).to_dyn()] #[create_dyn_unit_fn = |config, unit_index| alu_branch::AluBranch::new(config, unit_index).to_dyn()]
#[extract = alu_branch_mop] #[extract(alu_branch_mop, alu_branch_mop_sim, alu_branch_mop_sim_ref, alu_branch_mop_sim_mut)]
AluBranch(AluBranchMOp<DestReg, SrcRegWidth>), AluBranch(AluBranchMOp<DestReg, SrcRegWidth>),
#[transformed_move] #[transformed_move]
#[create_dyn_unit_fn = |config, unit_index| todo!()] #[create_dyn_unit_fn = |config, unit_index| todo!()]
#[extract = transformed_move_mop] #[extract(transformed_move_mop, transformed_move_mop_sim, transformed_move_mop_sim_ref, transformed_move_mop_sim_mut)]
TransformedMove(TransformedMoveOp), TransformedMove(TransformedMoveOp),
#[create_dyn_unit_fn = |config, unit_index| todo!()] #[create_dyn_unit_fn = |config, unit_index| todo!()]
#[extract = load_store_mop] #[extract(load_store_mop, load_store_mop_sim, load_store_mop_sim_ref, load_store_mop_sim_mut)]
LoadStore(LoadStoreMOp<DestReg, SrcRegWidth>), LoadStore(LoadStoreMOp<DestReg, SrcRegWidth>),
} }
} }
@ -277,9 +353,9 @@ pub struct UnitResultCompleted<ExtraOut> {
pub extra_out: ExtraOut, pub extra_out: ExtraOut,
} }
#[hdl(cmp_eq)] #[hdl(cmp_eq, no_static)]
pub struct UnitOutputWrite<OutRegNumWidth: Size> { pub struct UnitOutputWrite<C: PhantomConstGet<CpuConfig>> {
pub which: UnitOutRegNum<OutRegNumWidth>, pub which: UnitOutRegNum<C>,
pub value: PRegValue, pub value: PRegValue,
} }
@ -300,21 +376,21 @@ impl<ExtraOut: Type> UnitResult<ExtraOut> {
} }
} }
#[hdl] #[hdl(no_static)]
pub struct UnitOutput<OutRegNumWidth: Size, ExtraOut> { pub struct UnitOutput<C: PhantomConstGet<CpuConfig>, ExtraOut> {
pub which: UnitOutRegNum<OutRegNumWidth>, pub which: UnitOutRegNum<C>,
pub result: UnitResult<ExtraOut>, pub result: UnitResult<ExtraOut>,
} }
impl<OutRegNumWidth: Size, ExtraOut: Type> UnitOutput<OutRegNumWidth, ExtraOut> { impl<C: PhantomConstCpuConfig, ExtraOut: Type> UnitOutput<C, ExtraOut> {
pub fn extra_out_ty(self) -> ExtraOut { pub fn extra_out_ty(self) -> ExtraOut {
self.result.extra_out_ty() self.result.extra_out_ty()
} }
} }
#[hdl(cmp_eq)] #[hdl(cmp_eq, no_static)]
pub struct UnitCancelInput<OutRegNumWidth: Size> { pub struct UnitCancelInput<C: PhantomConstGet<CpuConfig>> {
pub which: UnitOutRegNum<OutRegNumWidth>, pub which: UnitOutRegNum<C>,
} }
pub trait UnitTrait: pub trait UnitTrait:
@ -332,7 +408,7 @@ pub trait UnitTrait:
fn extract_mop( fn extract_mop(
&self, &self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>, mop: Expr<RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>>; ) -> Expr<HdlOption<Self::MOp>>;
fn module(&self) -> Interned<Module<Self::Type>>; fn module(&self) -> Interned<Module<Self::Type>>;
@ -340,7 +416,7 @@ pub trait UnitTrait:
fn unit_to_reg_alloc( fn unit_to_reg_alloc(
&self, &self,
this: Expr<Self::Type>, this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>>; ) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>>;
fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>; fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>;
@ -390,7 +466,7 @@ impl UnitTrait for DynUnit {
fn extract_mop( fn extract_mop(
&self, &self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>, mop: Expr<RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>> { ) -> Expr<HdlOption<Self::MOp>> {
self.unit.extract_mop(mop) self.unit.extract_mop(mop)
} }
@ -402,7 +478,7 @@ impl UnitTrait for DynUnit {
fn unit_to_reg_alloc( fn unit_to_reg_alloc(
&self, &self,
this: Expr<Self::Type>, this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { ) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
self.unit.unit_to_reg_alloc(this) self.unit.unit_to_reg_alloc(this)
} }
@ -445,7 +521,7 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T
fn extract_mop( fn extract_mop(
&self, &self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>, mop: Expr<RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>> { ) -> Expr<HdlOption<Self::MOp>> {
Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop))) Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop)))
} }
@ -457,7 +533,7 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T
fn unit_to_reg_alloc( fn unit_to_reg_alloc(
&self, &self,
this: Expr<Self::Type>, this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { ) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
Expr::from_bundle(Expr::as_bundle( Expr::from_bundle(Expr::as_bundle(
self.0.unit_to_reg_alloc(Expr::from_bundle(this)), self.0.unit_to_reg_alloc(Expr::from_bundle(this)),
)) ))

View file

@ -19,16 +19,13 @@ use crate::{
}, },
}; };
use fayalite::{ use fayalite::{
intern::{Intern, Interned}, intern::Interned, module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid,
module::wire_with_loc,
prelude::*,
util::ready_valid::ReadyValid,
}; };
use std::{collections::HashMap, ops::RangeTo}; use std::{collections::HashMap, ops::RangeTo};
#[hdl] #[hdl]
fn add_sub<SrcCount: KnownSize>( fn add_sub<SrcCount: KnownSize>(
mop: Expr<AddSubMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>, mop: Expr<AddSubMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, SrcCount>>,
pc: Expr<UInt<64>>, pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
@ -245,7 +242,7 @@ fn add_sub<SrcCount: KnownSize>(
#[hdl] #[hdl]
fn logical_flags( fn logical_flags(
mop: Expr<LogicalFlagsMOp<UnitOutRegNum<DynSize>, DynSize>>, mop: Expr<LogicalFlagsMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> { ) -> Expr<UnitResultCompleted<()>> {
@ -259,7 +256,7 @@ fn logical_flags(
#[hdl] #[hdl]
fn logical( fn logical(
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<2>>>, mop: Expr<LogicalMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, ConstUsize<2>>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> { ) -> Expr<UnitResultCompleted<()>> {
@ -273,7 +270,7 @@ fn logical(
#[hdl] #[hdl]
fn logical_i( fn logical_i(
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<1>>>, mop: Expr<LogicalMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, ConstUsize<1>>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> { ) -> Expr<UnitResultCompleted<()>> {
@ -287,7 +284,7 @@ fn logical_i(
#[hdl] #[hdl]
fn shift_rotate( fn shift_rotate(
mop: Expr<ShiftRotateMOp<UnitOutRegNum<DynSize>, DynSize>>, mop: Expr<ShiftRotateMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> { ) -> Expr<UnitResultCompleted<()>> {
@ -301,7 +298,7 @@ fn shift_rotate(
#[hdl] #[hdl]
fn compare<SrcCount: KnownSize>( fn compare<SrcCount: KnownSize>(
mop: Expr<CompareMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>, mop: Expr<CompareMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, SrcCount>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> { ) -> Expr<UnitResultCompleted<()>> {
@ -315,7 +312,7 @@ fn compare<SrcCount: KnownSize>(
#[hdl] #[hdl]
fn branch<SrcCount: KnownSize>( fn branch<SrcCount: KnownSize>(
mop: Expr<BranchMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>, mop: Expr<BranchMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize, SrcCount>>,
pc: Expr<UInt<64>>, pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
@ -330,7 +327,7 @@ fn branch<SrcCount: KnownSize>(
#[hdl] #[hdl]
fn read_special( fn read_special(
mop: Expr<ReadSpecialMOp<UnitOutRegNum<DynSize>, DynSize>>, mop: Expr<ReadSpecialMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
pc: Expr<UInt<64>>, pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>, flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
@ -344,20 +341,18 @@ fn read_special(
} }
#[hdl_module] #[hdl_module]
pub fn alu_branch(config: &CpuConfig, unit_index: usize) { pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) {
#[hdl] #[hdl]
let cd: ClockDomain = m.input(); let cd: ClockDomain = m.input();
#[hdl] #[hdl]
let unit_to_reg_alloc: UnitToRegAlloc< let unit_to_reg_alloc: UnitToRegAlloc<
AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>, PhantomConst<CpuConfig>,
AluBranchMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>,
(), (),
DynSize, > = m.output(
DynSize, UnitToRegAlloc[config][AluBranchMOp[UnitOutRegNum[config]][config.get().p_reg_num_width()]]
DynSize, [()],
> = m.output(config.unit_to_reg_alloc( );
AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()],
(),
));
#[hdl] #[hdl]
let global_state: GlobalState = m.input(); let global_state: GlobalState = m.input();
@ -375,10 +370,11 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
#[hdl] #[hdl]
if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) { if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) {
#[hdl] #[hdl]
let ExecuteStart::<_> { let ExecuteStart::<_, _> {
mop, mop,
pc, pc,
src_values, src_values,
config: _,
} = execute_start; } = execute_start;
#[hdl] #[hdl]
match mop { match mop {
@ -580,14 +576,14 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct AluBranch { pub struct AluBranch {
config: Interned<CpuConfig>, config: PhantomConst<CpuConfig>,
module: Interned<Module<alu_branch>>, module: Interned<Module<alu_branch>>,
} }
impl AluBranch { impl AluBranch {
pub fn new(config: &CpuConfig, unit_index: usize) -> Self { pub fn new(config: PhantomConst<CpuConfig>, unit_index: usize) -> Self {
Self { Self {
config: config.intern(), config,
module: alu_branch(config, unit_index), module: alu_branch(config, unit_index),
} }
} }
@ -596,7 +592,7 @@ impl AluBranch {
impl UnitTrait for AluBranch { impl UnitTrait for AluBranch {
type Type = alu_branch; type Type = alu_branch;
type ExtraOut = (); type ExtraOut = ();
type MOp = AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>; type MOp = AluBranchMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>;
fn ty(&self) -> Self::Type { fn ty(&self) -> Self::Type {
self.module.io_ty() self.module.io_ty()
@ -616,7 +612,7 @@ impl UnitTrait for AluBranch {
fn extract_mop( fn extract_mop(
&self, &self,
mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>, mop: Expr<RenamedMOp<UnitOutRegNum<PhantomConst<CpuConfig>>, DynSize>>,
) -> Expr<HdlOption<Self::MOp>> { ) -> Expr<HdlOption<Self::MOp>> {
UnitMOp::alu_branch_mop(mop) UnitMOp::alu_branch_mop(mop)
} }
@ -628,7 +624,7 @@ impl UnitTrait for AluBranch {
fn unit_to_reg_alloc( fn unit_to_reg_alloc(
&self, &self,
this: Expr<Self::Type>, this: Expr<Self::Type>,
) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { ) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> {
this.unit_to_reg_alloc this.unit_to_reg_alloc
} }

View file

@ -2,7 +2,7 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
config::CpuConfig, config::{CpuConfig, CpuConfigUnitCount, PhantomConstCpuConfig},
instruction::{COMMON_MOP_SRC_LEN, MOpTrait, PRegNum, UnitNum, UnitOutRegNum}, instruction::{COMMON_MOP_SRC_LEN, MOpTrait, PRegNum, UnitNum, UnitOutRegNum},
register::PRegValue, register::PRegValue,
unit::{UnitCancelInput, UnitOutput, UnitOutputWrite}, unit::{UnitCancelInput, UnitOutput, UnitOutputWrite},
@ -15,13 +15,11 @@ use fayalite::{
ty::StaticType, ty::StaticType,
util::ready_valid::ReadyValid, util::ready_valid::ReadyValid,
}; };
use std::marker::PhantomData;
#[hdl] #[hdl(no_static)]
pub struct UnitForwardingInfo<UnitNumWidth: Size, OutRegNumWidth: Size, UnitCount: Size> { pub struct UnitForwardingInfo<C: PhantomConstGet<CpuConfig>> {
pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<OutRegNumWidth>>, UnitCount>, pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<C>>, CpuConfigUnitCount<C>>,
pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<OutRegNumWidth>>, UnitCount>, pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<C>>, CpuConfigUnitCount<C>>,
pub _phantom: PhantomData<UnitNumWidth>,
} }
#[hdl] #[hdl]
@ -30,26 +28,18 @@ pub struct UnitInput<MOp: Type> {
pub pc: UInt<64>, pub pc: UInt<64>,
} }
#[hdl] #[hdl(no_static)]
pub struct UnitToRegAlloc< pub struct UnitToRegAlloc<C: PhantomConstGet<CpuConfig>, MOp: Type, ExtraOut: Type> {
MOp: Type,
ExtraOut: Type,
UnitNumWidth: Size,
OutRegNumWidth: Size,
UnitCount: Size,
> {
#[hdl(flip)] #[hdl(flip)]
pub unit_forwarding_info: UnitForwardingInfo<UnitNumWidth, OutRegNumWidth, UnitCount>, pub unit_forwarding_info: UnitForwardingInfo<C>,
#[hdl(flip)] #[hdl(flip)]
pub input: ReadyValid<UnitInput<MOp>>, pub input: ReadyValid<UnitInput<MOp>>,
#[hdl(flip)] #[hdl(flip)]
pub cancel_input: HdlOption<UnitCancelInput<OutRegNumWidth>>, pub cancel_input: HdlOption<UnitCancelInput<C>>,
pub output: HdlOption<UnitOutput<OutRegNumWidth, ExtraOut>>, pub output: HdlOption<UnitOutput<C, ExtraOut>>,
} }
impl<MOp: Type, ExtraOut: Type, UnitNumWidth: Size, OutRegNumWidth: Size, UnitCount: Size> impl<C: PhantomConstCpuConfig, MOp: Type, ExtraOut: Type> UnitToRegAlloc<C, MOp, ExtraOut> {
UnitToRegAlloc<MOp, ExtraOut, UnitNumWidth, OutRegNumWidth, UnitCount>
{
pub fn mop_ty(self) -> MOp { pub fn mop_ty(self) -> MOp {
self.input.data.HdlSome.mop self.input.data.HdlSome.mop
} }
@ -58,16 +48,20 @@ impl<MOp: Type, ExtraOut: Type, UnitNumWidth: Size, OutRegNumWidth: Size, UnitCo
} }
} }
#[hdl] #[hdl(no_static)]
pub struct ExecuteStart<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>> { pub struct ExecuteStart<
C: PhantomConstGet<CpuConfig>,
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<C>>,
> {
pub mop: MOp, pub mop: MOp,
pub pc: UInt<64>, pub pc: UInt<64>,
pub src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>, pub src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>,
pub config: C,
} }
#[hdl] #[hdl(no_static)]
pub struct ExecuteEnd<OutRegNumWidth: Size, ExtraOut> { pub struct ExecuteEnd<C: PhantomConstGet<CpuConfig>, ExtraOut> {
pub unit_output: UnitOutput<OutRegNumWidth, ExtraOut>, pub unit_output: UnitOutput<C, ExtraOut>,
} }
#[hdl] #[hdl]
@ -240,10 +234,10 @@ impl InFlightOpsSummary<DynSize> {
#[hdl_module] #[hdl_module]
pub fn unit_base< pub fn unit_base<
MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>, MOp: Type + MOpTrait<DestReg = UnitOutRegNum<PhantomConst<CpuConfig>>, SrcRegWidth = DynSize>,
ExtraOut: Type, ExtraOut: Type,
>( >(
config: &CpuConfig, config: PhantomConst<CpuConfig>,
unit_index: usize, unit_index: usize,
mop_ty: MOp, mop_ty: MOp,
extra_out_ty: ExtraOut, extra_out_ty: ExtraOut,
@ -251,17 +245,18 @@ pub fn unit_base<
#[hdl] #[hdl]
let cd: ClockDomain = m.input(); let cd: ClockDomain = m.input();
#[hdl] #[hdl]
let unit_to_reg_alloc: UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> = let unit_to_reg_alloc: UnitToRegAlloc<PhantomConst<CpuConfig>, MOp, ExtraOut> =
m.output(config.unit_to_reg_alloc(mop_ty, extra_out_ty)); m.output(UnitToRegAlloc[config][mop_ty][extra_out_ty]);
#[hdl] #[hdl]
let execute_start: ReadyValid<ExecuteStart<MOp>> = m.output(ReadyValid[ExecuteStart[mop_ty]]); let execute_start: ReadyValid<ExecuteStart<PhantomConst<CpuConfig>, MOp>> =
m.output(ReadyValid[ExecuteStart[config][mop_ty]]);
#[hdl] #[hdl]
let execute_end: HdlOption<ExecuteEnd<DynSize, ExtraOut>> = let execute_end: HdlOption<ExecuteEnd<PhantomConst<CpuConfig>, ExtraOut>> =
m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][extra_out_ty]]); m.input(HdlOption[ExecuteEnd[config][extra_out_ty]]);
connect(execute_start.data, execute_start.ty().data.HdlNone()); connect(execute_start.data, execute_start.ty().data.HdlNone());
let max_in_flight = config.unit_max_in_flight(unit_index).get(); let max_in_flight = config.get().unit_max_in_flight(unit_index).get();
let in_flight_op_ty = InFlightOp[mop_ty]; let in_flight_op_ty = InFlightOp[mop_ty];
#[hdl] #[hdl]
let in_flight_ops = reg_builder() let in_flight_ops = reg_builder()
@ -279,16 +274,15 @@ pub fn unit_base<
); );
#[hdl] #[hdl]
let UnitForwardingInfo::<_, _, _> { let UnitForwardingInfo::<_> {
unit_output_writes, unit_output_writes,
unit_reg_frees, unit_reg_frees,
_phantom: _,
} = unit_to_reg_alloc.unit_forwarding_info; } = unit_to_reg_alloc.unit_forwarding_info;
#[hdl] #[hdl]
let read_src_regs = wire(mop_ty.src_regs_ty()); let read_src_regs = wire(mop_ty.src_regs_ty());
connect( connect(
read_src_regs, read_src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
); );
#[hdl] #[hdl]
let read_src_values = wire(); let read_src_values = wire();
@ -297,7 +291,7 @@ pub fn unit_base<
let input_src_regs = wire(mop_ty.src_regs_ty()); let input_src_regs = wire(mop_ty.src_regs_ty());
connect( connect(
input_src_regs, input_src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
); );
#[hdl] #[hdl]
let input_src_regs_valid = wire(); let input_src_regs_valid = wire();
@ -309,7 +303,7 @@ pub fn unit_base<
Bool, Bool,
SourceLocation::caller(), SourceLocation::caller(),
); );
mem.depth(1 << config.out_reg_num_width); mem.depth(1 << config.get().out_reg_num_width);
mem mem
}) })
.collect(); .collect();
@ -319,11 +313,11 @@ pub fn unit_base<
PRegValue, PRegValue,
SourceLocation::caller(), SourceLocation::caller(),
); );
unit_output_regs.depth(1 << config.out_reg_num_width); unit_output_regs.depth(1 << config.get().out_reg_num_width);
for src_index in 0..COMMON_MOP_SRC_LEN { for src_index in 0..COMMON_MOP_SRC_LEN {
let read_port = unit_output_regs.new_read_port(); let read_port = unit_output_regs.new_read_port();
let p_reg_num = read_src_regs[src_index].cast_bits_to(config.p_reg_num()); let p_reg_num = read_src_regs[src_index].cast_bits_to(PRegNum[config]);
connect_any(read_port.addr, p_reg_num.unit_out_reg.value); connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
connect(read_port.en, false); connect(read_port.en, false);
connect(read_port.clk, cd.clk); connect(read_port.clk, cd.clk);
@ -336,7 +330,7 @@ pub fn unit_base<
for src_index in 0..COMMON_MOP_SRC_LEN { for src_index in 0..COMMON_MOP_SRC_LEN {
let read_port = unit_output_regs_valid[unit_index].new_read_port(); let read_port = unit_output_regs_valid[unit_index].new_read_port();
let p_reg_num = input_src_regs[src_index].cast_bits_to(config.p_reg_num()); let p_reg_num = input_src_regs[src_index].cast_bits_to(PRegNum[config]);
connect_any(read_port.addr, p_reg_num.unit_out_reg.value); connect_any(read_port.addr, p_reg_num.unit_out_reg.value);
connect(read_port.en, false); connect(read_port.en, false);
connect(read_port.clk, cd.clk); connect(read_port.clk, cd.clk);
@ -367,8 +361,8 @@ pub fn unit_base<
connect_any(ready_write_port.addr, unit_output_write.which.value); connect_any(ready_write_port.addr, unit_output_write.which.value);
connect(ready_write_port.en, true); connect(ready_write_port.en, true);
let p_reg_num = #[hdl] let p_reg_num = #[hdl]
PRegNum::<_, _> { PRegNum::<_> {
unit_num: config.unit_num().from_index(unit_index), unit_num: UnitNum[config].from_index(unit_index),
unit_out_reg: unit_output_write.which, unit_out_reg: unit_output_write.which,
}; };
for src_index in 0..COMMON_MOP_SRC_LEN { for src_index in 0..COMMON_MOP_SRC_LEN {
@ -399,10 +393,11 @@ pub fn unit_base<
execute_start.data, execute_start.data,
HdlSome( HdlSome(
#[hdl] #[hdl]
ExecuteStart::<_> { ExecuteStart::<_, _> {
mop: in_flight_op.mop, mop: in_flight_op.mop,
pc: in_flight_op.pc, pc: in_flight_op.pc,
src_values: read_src_values, src_values: read_src_values,
config,
}, },
), ),
); );
@ -425,7 +420,7 @@ pub fn unit_base<
let input_mop_src_regs = wire(mop_ty.src_regs_ty()); let input_mop_src_regs = wire(mop_ty.src_regs_ty());
connect( connect(
input_mop_src_regs, input_mop_src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
); );
MOp::connect_src_regs(mop, input_mop_src_regs); MOp::connect_src_regs(mop, input_mop_src_regs);
let src_ready_flags = wire_with_loc( let src_ready_flags = wire_with_loc(
@ -497,7 +492,7 @@ pub fn unit_base<
); );
connect( connect(
src_regs, src_regs,
repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize),
); );
MOp::connect_src_regs(mop, src_regs); MOp::connect_src_regs(mop, src_regs);
@ -521,8 +516,8 @@ pub fn unit_base<
value: _, value: _,
} = unit_output_write; } = unit_output_write;
let p_reg_num = #[hdl] let p_reg_num = #[hdl]
PRegNum::<_, _> { PRegNum::<_> {
unit_num: config.unit_num().from_index(unit_index), unit_num: UnitNum[config].from_index(unit_index),
unit_out_reg, unit_out_reg,
}; };
for src_index in 0..COMMON_MOP_SRC_LEN { for src_index in 0..COMMON_MOP_SRC_LEN {

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
// 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
#![cfg(todo)]
use cpu::{ use cpu::{
config::{CpuConfig, UnitConfig}, config::{CpuConfig, UnitConfig},

View file

@ -0,0 +1,924 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use cpu::{
config::{CpuConfig, CpuConfigFetchWidth, PhantomConstCpuConfig, UnitConfig},
instruction::{
AddSubMOp, AluBranchMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LoadMOp,
LoadStoreConversion, LoadStoreWidth, MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp,
OutputIntegerMode, StoreMOp,
},
rename_execute_retire::{
MOpInstance, PostDecodeOutputInterface, RetireToNextPcInterface, rename_execute_retire,
},
unit::UnitKind,
util::array_vec::ArrayVec,
};
use fayalite::{
prelude::*,
sim::vcd::VcdWriterDecls,
ty::{OpaqueSimValue, StaticType},
util::RcWriter,
};
use std::{
cell::Cell,
collections::{BTreeMap, VecDeque},
fmt::{self, Write},
mem,
num::NonZeroUsize,
};
fn zeroed<T: Type>(ty: T) -> SimValue<T> {
SimValue::from_opaque(
ty,
OpaqueSimValue::from_bits(UInt::new(ty.canonical().bit_width()).zero()),
)
}
fn signed_hex(v: impl Into<i64>) -> impl fmt::Display {
let v = v.into();
fmt::from_fn(move |f| {
if v < 0 {
f.write_char('-')?;
}
write!(f, "{:#x}", v.unsigned_abs())
})
}
struct RandomState {
state: Cell<u64>,
}
impl RandomState {
fn random_u64(&self, key: u32) -> u64 {
let state = self.state.replace(self.state.get().wrapping_add(1));
// make a pseudo-random number deterministically based on state and key
let mut random = state
.wrapping_add(1)
.wrapping_mul(0x39FF446D8BFB75BB) // random prime
.rotate_left(32)
.wrapping_mul(0x73161B54984B1C21) // random prime
.rotate_right(60);
random ^= key as u64;
random
.wrapping_mul(0x39FF446D8BFB75BB) // random prime
.rotate_left(32)
.wrapping_mul(0x73161B54984B1C21) // random prime
.rotate_right(60)
}
}
const START_PC: u64 = 0x0; // match microwatt's reset pc
#[derive(Clone, Debug)]
struct Insn<MOps = Vec<SimValue<self::MOp>>> {
size_in_bytes: u8,
power_isa: String,
mops: MOps,
}
enum LazyMOps {
MOps(Vec<SimValue<self::MOp>>),
Lazy(Box<dyn FnOnce(&[InsnsBuilderLabelState]) -> Vec<SimValue<self::MOp>>>),
}
impl Insn<LazyMOps> {
fn new<I: IntoIterator<Item: ToSimValue<Type = MOp>>>(
size_in_bytes: u8,
power_isa: String,
mops: I,
) -> Self {
Self {
size_in_bytes,
power_isa,
mops: LazyMOps::MOps(mops.into_iter().map(|mop| mop.into_sim_value()).collect()),
}
}
fn new_lazy<I: IntoIterator<Item: ToSimValue<Type = MOp>>>(
size_in_bytes: u8,
power_isa: String,
lazy_mops: impl FnOnce(&[InsnsBuilderLabelState]) -> I + 'static,
) -> Self {
Self {
size_in_bytes,
power_isa,
mops: LazyMOps::Lazy(Box::new(|labels| {
lazy_mops(labels)
.into_iter()
.map(|mop| mop.into_sim_value())
.collect()
})),
}
}
}
struct Insns {
insns: BTreeMap<u64, Insn>,
}
impl Insns {
fn get_mop_mut(&mut self, pc: u64, mop_index: usize) -> Option<&mut SimValue<MOp>> {
self.insns.get_mut(&pc)?.mops.get_mut(mop_index)
}
}
impl fmt::Debug for Insns {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map()
.entries(
self.insns
.iter()
.map(|(k, v)| (fmt::from_fn(move |f| write!(f, "{k:#x}")), v)),
)
.finish()
}
}
struct InsnsBuilderLabelState {
name: &'static str,
pc: Option<u64>,
location: &'static std::panic::Location<'static>,
}
impl InsnsBuilderLabelState {
fn pc(&self) -> u64 {
match self.pc {
Some(pc) => pc,
None => panic!("label not defined {}: at: {}", self.name, self.location),
}
}
}
#[derive(Clone, Copy, Debug)]
struct InsnsBuilderLabel(usize);
struct InsnsBuilder {
labels: Vec<InsnsBuilderLabelState>,
insns: BTreeMap<u64, Insn<LazyMOps>>,
pc: u64,
}
impl InsnsBuilder {
fn new() -> Self {
Self {
labels: Vec::new(),
insns: BTreeMap::new(),
pc: START_PC,
}
}
#[track_caller]
fn new_label(&mut self, name: &'static str) -> InsnsBuilderLabel {
let retval = InsnsBuilderLabel(self.labels.len());
self.labels.push(InsnsBuilderLabelState {
pc: None,
name,
location: std::panic::Location::caller(),
});
retval
}
#[track_caller]
fn define_label(&mut self, label: InsnsBuilderLabel) {
let label_state = &mut self.labels[label.0];
if label_state.pc.is_some() {
panic!("label already defined at: {}", label_state.location);
}
label_state.pc = Some(self.pc);
label_state.location = std::panic::Location::caller();
}
#[track_caller]
fn new_defined_label(&mut self, name: &'static str) -> InsnsBuilderLabel {
let label = self.new_label(name);
self.define_label(label);
label
}
#[track_caller]
fn add_insn(&mut self, insn: Insn<LazyMOps>) {
let pc = self.pc;
let next_pc = pc.wrapping_add(insn.size_in_bytes as u64);
if self.insns.insert(self.pc, insn).is_some() {
panic!("instruction already stored at {pc:#x}");
}
self.pc = next_pc;
}
fn build(self) -> Insns {
Insns {
insns: self
.insns
.into_iter()
.map(|(pc, insn)| {
let Insn {
size_in_bytes,
power_isa,
mops,
} = insn;
(
pc,
Insn {
size_in_bytes,
power_isa,
mops: match mops {
LazyMOps::MOps(mops) => mops,
LazyMOps::Lazy(f) => f(&self.labels),
},
},
)
})
.collect(),
}
}
fn set_pc(&mut self, pc: u64) {
self.pc = pc;
}
fn power_isa_b(&mut self, target: InsnsBuilderLabel) {
let pc = self.pc;
self.add_insn(Insn::new_lazy(
4,
format!("b {}", self.labels[target.0].name),
move |labels| {
[BranchMOp::branch_i(
MOpDestReg::new([], []),
MOpRegNum::const_zero().value,
labels[target.0]
.pc()
.wrapping_sub(pc)
.cast_to_static::<SInt<_>>(),
true,
false,
false,
)]
},
));
}
fn power_isa_blr(&mut self) {
self.add_insn(Insn::new(
4,
format!("blr"),
[BranchMOp::branch_i(
MOpDestReg::new([], []),
MOpRegNum::power_isa_lr_reg().value,
0i8.cast_to_static::<SInt<_>>(),
false,
false,
true,
)],
));
}
fn power_isa_bgt(&mut self, target: InsnsBuilderLabel) {
let pc = self.pc;
self.add_insn(Insn::new_lazy(
4,
format!("bgt {}", self.labels[target.0].name),
move |labels| {
[BranchMOp::branch_cond_ctr(
MOpDestReg::new([], []),
[
MOpRegNum::power_isa_cr_reg_imm(0).value,
MOpRegNum::const_zero().value,
MOpRegNum::const_zero().value,
],
labels[target.0]
.pc()
.wrapping_sub(pc)
.cast_to_static::<SInt<_>>(),
false,
ConditionMode.SGt(),
false,
true,
false,
false,
)]
},
));
}
fn power_isa_bl(&mut self, target: InsnsBuilderLabel) {
let pc = self.pc;
self.add_insn(Insn::new_lazy(
4,
format!("bl {}", self.labels[target.0].name),
move |labels| {
[BranchMOp::branch_i(
MOpDestReg::new([MOpRegNum::power_isa_lr_reg()], []),
MOpRegNum::const_zero().value,
labels[target.0]
.pc()
.wrapping_sub(pc)
.cast_to_static::<SInt<_>>(),
true,
true,
false,
)]
},
));
}
fn power_isa_add(&mut self, dest: usize, src1: usize, src2: usize) {
self.add_insn(Insn::new(
4,
format!("add {dest}, {src1}, {src2}"),
[AddSubMOp::add_sub(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
[
MOpRegNum::power_isa_gpr_reg_imm(src1).value,
MOpRegNum::power_isa_gpr_reg_imm(src2).value,
MOpRegNum::const_zero().value,
],
0i8.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
)],
));
}
fn power_isa_addi(&mut self, dest: usize, src: usize, imm: i16) {
self.add_insn(Insn::new(
4,
format!("addi {dest}, {src}, {}", signed_hex(imm)),
[AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
[
MOpRegNum::power_isa_gpr_or_zero_reg_imm(src).value,
MOpRegNum::const_zero().value,
],
imm.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
)],
));
}
fn power_isa_cmpldi(&mut self, dest: usize, src: usize, imm: u16) {
self.add_insn(Insn::new(
4,
format!("cmpldi {dest}, {src}, {imm:#x}"),
[CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(src).value],
imm.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.U64(),
)],
));
}
fn power_isa_ld(&mut self, dest: usize, src: usize, disp: i16) {
self.add_insn(Insn::new(
4,
format!("ld {dest}, {}({src})", signed_hex(disp)),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_or_zero_reg_imm(src).value,
MOpRegNum::const_zero().value,
],
disp.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
LoadMOp::load(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
[MOpRegNum::power_isa_temp_reg().value],
LoadStoreWidth.Width64Bit(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
fn power_isa_std(&mut self, value_src: usize, addr_src: usize, disp: i16) {
self.add_insn(Insn::new(
4,
format!("std {value_src}, {}({addr_src})", signed_hex(disp)),
[
AddSubMOp::add_sub_i(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
[
MOpRegNum::power_isa_gpr_or_zero_reg_imm(addr_src).value,
MOpRegNum::const_zero().value,
],
disp.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
false,
false,
false,
false,
),
StoreMOp::store(
MOpDestReg::new_sim(&[], &[]),
[
MOpRegNum::power_isa_temp_reg().value,
MOpRegNum::power_isa_gpr_reg_imm(value_src).value,
],
LoadStoreWidth.Width64Bit(),
LoadStoreConversion.ZeroExt(),
),
],
));
}
fn power_isa_mflr(&mut self, dest: usize) {
self.add_insn(Insn::new(
4,
format!("mflr {dest}"),
[MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
[MOpRegNum::power_isa_lr_reg().value],
0i8.cast_to_static::<SInt<_>>(),
)],
));
}
fn power_isa_mtlr(&mut self, src: usize) {
self.add_insn(Insn::new(
4,
format!("mtlr {src}"),
[MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(src).value],
0i8.cast_to_static::<SInt<_>>(),
)],
));
}
fn power_isa_mr(&mut self, dest: usize, src: usize) {
self.add_insn(Insn::new(
4,
format!("mr {dest}, {src}"),
[MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(dest)], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(src).value],
0i8.cast_to_static::<SInt<_>>(),
)],
));
}
}
trait MakeInsns {
fn make_insns() -> Insns;
}
struct FibonacciInsns;
impl MakeInsns for FibonacciInsns {
fn make_insns() -> Insns {
let mut b = InsnsBuilder::new();
let fib = b.new_label("fib");
let fib_small = b.new_label("fib_small");
b.power_isa_ld(3, 0, -16); // load input
b.power_isa_addi(1, 0, 0x4000); // setup stack pointer
b.power_isa_bl(fib);
b.power_isa_std(3, 0, -16); // store output
let done = b.new_defined_label("done");
b.power_isa_b(done);
b.set_pc(0x1000);
b.define_label(fib);
b.power_isa_cmpldi(0, 3, 1);
b.power_isa_bgt(fib_small); // if input > 1 goto fib_small
// push stack frame
b.power_isa_mflr(0);
b.power_isa_addi(1, 1, -16);
b.power_isa_std(0, 1, 0);
b.power_isa_std(30, 1, 8);
b.power_isa_addi(30, 3, -2);
b.power_isa_addi(3, 3, -1);
b.power_isa_bl(fib); // fib(input - 1)
b.power_isa_mr(2, 3);
b.power_isa_mr(3, 30);
b.power_isa_mr(30, 2);
b.power_isa_bl(fib); // fib(input - 2)
b.power_isa_add(3, 3, 30); // set output to fib(input - 1) + fib(input - 2)
// pop stack frame and return
b.power_isa_ld(0, 1, 0);
b.power_isa_ld(30, 1, 8);
b.power_isa_addi(1, 1, 16);
b.power_isa_mtlr(0);
b.power_isa_blr();
b.define_label(fib_small);
b.power_isa_addi(3, 0, 1); // set output to 1
b.power_isa_blr(); // return
b.build()
}
}
#[hdl(no_static)]
struct MockNextPcDebugState<C: PhantomConstGet<CpuConfig>> {
next_pc: UInt<64>,
next_mop_index: UInt<8>,
next_br_pred_state: BrPredDebugState,
next_id: UInt<64>,
fetch_block_id: UInt<64>,
fetch_queue: ArrayVec<(BrPredDebugState, MOpInstance<MOp>), CpuConfigFetchWidth<C>>,
config: C,
}
#[hdl]
struct BrPredDebugState {
call_stack: ArrayVec<UInt<64>, ConstUsize<{ BrPredState::CALL_STACK_CAPACITY }>>,
}
#[derive(Copy, Clone)]
struct BrPredState {
call_stack: [u64; Self::CALL_STACK_CAPACITY],
call_stack_len: usize,
}
impl BrPredState {}
impl fmt::Debug for BrPredState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
call_stack,
call_stack_len,
} = self;
f.debug_struct("BrPredState")
.field(
"call_stack",
&fmt::from_fn(|f| {
let mut debug_list = f.debug_list();
for pc in &call_stack[..*call_stack_len] {
debug_list.entry(&fmt::from_fn(|f| write!(f, "{pc:#x}")));
}
debug_list.finish()
}),
)
.finish()
}
}
impl BrPredState {
const CALL_STACK_CAPACITY: usize = 8;
fn new() -> Self {
Self {
call_stack: [0; _],
call_stack_len: 0,
}
}
fn predict_call(&mut self, return_pc: u64) {
if self.call_stack_len == Self::CALL_STACK_CAPACITY {
self.call_stack.copy_within(1.., 0);
*self.call_stack.last_mut().expect("known to be non-zero") = return_pc;
} else {
self.call_stack[self.call_stack_len] = return_pc;
self.call_stack_len += 1;
}
}
fn predict_ret(&mut self) -> Option<u64> {
if self.call_stack_len > 0 {
self.call_stack_len -= 1;
Some(mem::replace(&mut self.call_stack[self.call_stack_len], 0))
} else {
None
}
}
fn predict_branch<SrcCount: KnownSize>(
&mut self,
mop: &SimValue<BranchMOp<MOpDestReg, ConstUsize<{ MOpRegNum::WIDTH }>, SrcCount>>,
branch_pc: u64,
fallthrough_pc: u64,
) -> u64 {
if *mop.is_ret {
if let Some(retval) = self.predict_ret() {
return retval;
}
}
let mut src_regs = [MOpRegNum::CONST_ZERO_REG_NUM; 3];
MOpTrait::for_each_src_reg_sim_ref(mop, &mut |reg, index| {
src_regs[index] = reg.cast_to_static::<UInt<32>>().as_int();
});
if src_regs[1] != MOpRegNum::CONST_ZERO_REG_NUM {
// indirect branch -- this test doesn't implement predicting them, so just use the fallthrough_pc
return fallthrough_pc;
}
let target_pc = (*mop.common.imm)
.cast_to_static::<UInt<64>>()
.as_int()
.wrapping_add(if *mop.pc_relative { branch_pc } else { 0 });
if *mop.is_call {
self.predict_call(fallthrough_pc);
}
if src_regs[0] != MOpRegNum::CONST_ZERO_REG_NUM
|| src_regs[2] != MOpRegNum::CONST_ZERO_REG_NUM
{
// conditional branch -- this test just predicts taken for backward branches and not taken for forward branches
if target_pc < fallthrough_pc {
target_pc
} else {
fallthrough_pc
}
} else {
target_pc
}
}
#[hdl]
fn debug_state(&self) -> SimValue<BrPredDebugState> {
let Self {
call_stack,
call_stack_len,
} = self;
#[hdl(sim)]
BrPredDebugState {
call_stack: BrPredDebugState
.call_stack
.from_iter_sim(0u64, &call_stack[..*call_stack_len])
.expect("known to fit"),
}
}
}
struct MockNextPcState<'a, C: PhantomConstCpuConfig> {
random_state: &'a RandomState,
insns: &'a Insns,
next_pc: u64,
next_mop_index: usize,
next_br_pred_state: BrPredState,
next_id: u64,
fetch_block_id: u64,
fetch_queue: VecDeque<(BrPredState, SimValue<MOpInstance<MOp>>)>,
config: C,
}
impl<'a, C: PhantomConstCpuConfig> MockNextPcState<'a, C> {
fn new(random_state: &'a RandomState, insns: &'a Insns, config: C) -> Self {
Self {
random_state,
insns,
next_pc: START_PC,
next_mop_index: 0,
next_br_pred_state: BrPredState::new(),
next_id: 0,
fetch_block_id: 0,
fetch_queue: VecDeque::with_capacity(config.get().fetch_width.get()),
config,
}
}
#[hdl]
fn debug_state(&self) -> SimValue<MockNextPcDebugState<C>> {
let Self {
random_state: _,
insns: _,
config,
next_pc,
next_mop_index,
next_br_pred_state,
next_id,
fetch_block_id,
fetch_queue,
} = self;
let ty = MockNextPcDebugState[*config];
#[hdl(sim)]
MockNextPcDebugState::<_> {
next_pc,
next_mop_index: *next_mop_index as u8,
next_br_pred_state: next_br_pred_state.debug_state(),
next_id,
fetch_block_id,
fetch_queue: ty
.fetch_queue
.from_iter_sim(
zeroed(StaticType::TYPE),
fetch_queue
.iter()
.map(|(br_pred_state, mop)| (br_pred_state.debug_state(), mop)),
)
.expect("known to fit"),
config,
}
}
#[hdl]
fn try_push_fetch_queue(&mut self) -> Result<(), ()> {
if self.fetch_queue.len() >= self.config.get().fetch_width.get() {
return Err(());
}
let insn = self.insns.insns.get(&self.next_pc).ok_or(())?;
let fallthrough_pc = self.next_pc.wrapping_add(insn.size_in_bytes.into());
let is_last_mop_index = self.next_mop_index + 1 >= insn.mops.len();
let mut predicted_next_pc = if is_last_mop_index {
fallthrough_pc
} else {
self.next_pc
};
let prev_br_pred_state = self.next_br_pred_state;
let mop = &insn.mops[self.next_mop_index];
#[hdl(sim)]
if let MOp::AluBranch(mop) = mop {
#[hdl(sim)]
match mop {
AluBranchMOp::<_, _>::Branch(mop) => {
predicted_next_pc =
self.next_br_pred_state
.predict_branch(mop, self.next_pc, fallthrough_pc);
}
AluBranchMOp::<_, _>::BranchI(mop) => {
predicted_next_pc =
self.next_br_pred_state
.predict_branch(mop, self.next_pc, fallthrough_pc);
}
_ => {}
}
}
let mop = #[hdl(sim)]
MOpInstance::<_> {
fetch_block_id: self.fetch_block_id.cast_to_static::<UInt<_>>(),
id: self.next_id.cast_to_static::<UInt<_>>(),
pc: self.next_pc,
predicted_next_pc,
size_in_bytes: insn.size_in_bytes.cast_to_static::<UInt<_>>(),
is_first_mop_in_insn: self.next_mop_index == 0,
mop,
};
println!("pushed to fetch queue: {mop:?}");
self.fetch_queue.push_back((prev_br_pred_state, mop));
if is_last_mop_index {
self.next_mop_index = 0;
self.next_pc = predicted_next_pc;
} else {
self.next_mop_index += 1;
}
Ok(())
}
fn peek_post_decode_output_insns(
&self,
) -> SimValue<ArrayVec<MOpInstance<MOp>, CpuConfigFetchWidth<C>>> {
let ty = ArrayVec[MOpInstance[MOp]][CpuConfigFetchWidth[self.config]];
let peek_size = self.random_state.random_u64(u32::from_le_bytes(*b"pdoi")) as usize
% (ty.capacity() + 1);
ty.from_iter_sim(
zeroed(MOpInstance[MOp]),
self.fetch_queue.iter().map(|(_, v)| v).take(peek_size),
)
.ok()
.expect("known to fit")
}
}
#[hdl_module(extern)]
fn mock_next_pc<#[hdl(skip)] MI: MakeInsns>(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let post_decode_output: PostDecodeOutputInterface<PhantomConst<CpuConfig>> =
m.output(PostDecodeOutputInterface[config]);
#[hdl]
let from_retire: RetireToNextPcInterface<PhantomConst<CpuConfig>> =
m.input(RetireToNextPcInterface[config]);
#[hdl]
let debug_state: MockNextPcDebugState<PhantomConst<CpuConfig>> =
m.output(MockNextPcDebugState[config]);
m.register_clock_for_past(cd.clk);
m.extern_module_simulation_fn(
(cd, post_decode_output, from_retire, debug_state),
|args, mut sim| async move {
let (cd, post_decode_output, from_retire, debug_state) = args;
// intentionally have a different sequence each time we're reset
let random_state = RandomState {
state: Cell::new(0),
};
let insns = MI::make_insns();
sim.resettable(
cd,
async |mut sim| {
sim.write(
post_decode_output.insns,
post_decode_output
.ty()
.insns
.new_sim(zeroed(StaticType::TYPE)),
)
.await;
sim.write(
post_decode_output.cancel.data,
#[hdl(sim)]
HdlNone(),
)
.await;
sim.write(from_retire.inner.ready, false).await;
},
|sim, ()| {
run_fn(
cd,
post_decode_output,
from_retire,
debug_state,
&random_state,
&insns,
sim,
)
},
)
.await;
},
);
#[hdl]
async fn run_fn(
cd: Expr<ClockDomain>,
post_decode_output: Expr<PostDecodeOutputInterface<PhantomConst<CpuConfig>>>,
from_retire: Expr<RetireToNextPcInterface<PhantomConst<CpuConfig>>>,
debug_state: Expr<MockNextPcDebugState<PhantomConst<CpuConfig>>>,
random_state: &RandomState,
insns: &Insns,
mut sim: ExternModuleSimulationState,
) {
let config = post_decode_output.ty().config;
let mut state = MockNextPcState::new(random_state, insns, config);
loop {
while state.try_push_fetch_queue().is_ok() {}
state.fetch_block_id += 1;
let post_decode_output_insns = state.peek_post_decode_output_insns();
sim.write(post_decode_output.insns, &post_decode_output_insns)
.await;
sim.write(
post_decode_output.cancel.data,
#[hdl(sim)]
HdlNone(),
)
.await;
sim.write(from_retire.inner.ready, false).await;
sim.write(debug_state, state.debug_state()).await;
sim.wait_for_clock_edge(cd.clk).await;
let ready = *sim.read_past(post_decode_output.ready, cd.clk).await;
for _ in ArrayVec::elements_sim_ref(&post_decode_output_insns)
.iter()
.take(ready)
{
let Some((prev_br_pred_state, started)) = state.fetch_queue.pop_front() else {
unreachable!();
};
println!("started: {started:?}\nprev_br_pred_state: {prev_br_pred_state:?}");
}
}
}
}
#[hdl_module]
fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let next_pc = instance(mock_next_pc::<MI>(config));
connect(next_pc.cd, cd);
#[hdl]
let dut = instance(rename_execute_retire(config));
connect(dut.cd, cd);
connect(dut.from_post_decode, next_pc.post_decode_output);
connect(next_pc.from_retire, dut.to_next_pc);
}
#[hdl]
#[test]
fn test_rename_execute_retire_fibonacci() {
let _n = SourceLocation::normalize_files_for_tests();
let mut config = CpuConfig::new(
vec![
UnitConfig::new(UnitKind::AluBranch),
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(3).unwrap();
let m = rename_execute_retire_test_harness::<FibonacciInsns>(PhantomConst::new_sized(config));
let mut sim = Simulation::new(m);
let writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
struct DumpVcdOnDrop {
writer: Option<RcWriter>,
}
impl Drop for DumpVcdOnDrop {
fn drop(&mut self) {
if let Some(mut writer) = self.writer.take() {
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
}
}
}
let mut writer = DumpVcdOnDrop {
writer: Some(writer),
};
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
for cycle in 0..50 {
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);
}
panic!();
// FIXME: vcd is just whatever rename_execute_retire does now, which isn't known to be correct
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("expected/rename_execute_retire_fibonacci.vcd") {
panic!();
}
}