Compare commits
1 commit
3300e7ca01
...
04e924460a
| Author | SHA1 | Date | |
|---|---|---|---|
| 04e924460a |
5 changed files with 44764 additions and 226112 deletions
|
|
@ -20,7 +20,7 @@ use crate::{
|
|||
CpuConfig, CpuConfigFetchWidth, CpuConfigMaxFetchesInFlight, CpuConfigRobSize,
|
||||
PhantomConstCpuConfig, TwiceCpuConfigFetchWidth,
|
||||
},
|
||||
util::array_vec::ArrayVec,
|
||||
util::{LFSR31, array_vec::ArrayVec},
|
||||
};
|
||||
use fayalite::{
|
||||
int::{UIntInRange, UIntInRangeInclusive, UIntInRangeInclusiveType, UIntInRangeType},
|
||||
|
|
@ -2955,33 +2955,9 @@ impl BTBEntry {
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
struct LFSR31 {
|
||||
// MSB is always zero, 32 bits makes it easier to manipulate
|
||||
state: UInt<32>,
|
||||
}
|
||||
|
||||
impl SimValueDefault for LFSR31 {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
Self { state: 1u32 }
|
||||
}
|
||||
}
|
||||
|
||||
impl LFSR31 {
|
||||
fn next(this: &mut SimValue<Self>) -> u32 {
|
||||
let state = this.state.as_int();
|
||||
let state = if state == 0 {
|
||||
1u32
|
||||
} else {
|
||||
// a maximal-length 31-bit LFSR
|
||||
let lsb = ((state >> 30) ^ (state >> 27)) & 1;
|
||||
let msb = (state << 1) & ((1 << 31) - 1);
|
||||
lsb | msb
|
||||
};
|
||||
*this.state = state.into();
|
||||
state
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2995,7 +2971,7 @@ impl BranchTargetBuffer {
|
|||
const LOG2_SIZE: usize = 4;
|
||||
const SIZE: usize = 1 << Self::LOG2_SIZE;
|
||||
fn next_index_to_replace(this: &mut SimValue<Self>) -> usize {
|
||||
LFSR31::next(&mut this.next_index_to_replace_lfsr) as usize % Self::SIZE
|
||||
LFSR31::next_sim(&mut this.next_index_to_replace_lfsr) as usize % Self::SIZE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
register::PRegValue,
|
||||
rename_execute_retire::to_unit_interfaces::ExecuteToUnitInterfaces,
|
||||
unit::{UnitKind, UnitMOp},
|
||||
util::array_vec::ArrayVec,
|
||||
util::{LFSR31, array_vec::ArrayVec},
|
||||
};
|
||||
use fayalite::{
|
||||
int::UIntInRangeInclusiveType,
|
||||
|
|
@ -227,6 +227,7 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RenameExecuteRetireDebugState
|
|||
next_pc_canceling,
|
||||
unit_canceling,
|
||||
l1_reg_file,
|
||||
lfsr: _,
|
||||
per_insn_timeline,
|
||||
} = self;
|
||||
let empty_string = SimOnlyValue::new(String::new());
|
||||
|
|
@ -239,6 +240,7 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RenameExecuteRetireDebugState
|
|||
next_pc_canceling: zeroed(next_pc_canceling),
|
||||
unit_canceling: zeroed(unit_canceling),
|
||||
l1_reg_file: zeroed(l1_reg_file),
|
||||
lfsr: LFSR31::new(),
|
||||
per_insn_timeline: SimValue::from_array_elements(
|
||||
per_insn_timeline,
|
||||
(0..per_insn_timeline.len()).map(|_| empty_string.clone()),
|
||||
|
|
@ -978,9 +980,15 @@ const SimOnlyString: SimOnlyString = SimOnlyString::TYPE;
|
|||
#[hdl(get(|c| c.rob_size.get().next_power_of_two()))]
|
||||
type PerInsnTimelineLen<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl]
|
||||
struct RenameDelayedEntry {
|
||||
mop: MOpInstance<MOp>,
|
||||
started_l2_store: Bool,
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
pub struct RenameExecuteRetireDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
rename_delayed: ArrayVec<MOpInstance<MOp>, TwiceCpuConfigFetchWidth<C>>,
|
||||
rename_delayed: ArrayVec<RenameDelayedEntry, TwiceCpuConfigFetchWidth<C>>,
|
||||
rename_table: RenameTableDebugState<C>,
|
||||
retire_rename_table: RenameTableDebugState<C>,
|
||||
rob: ReorderBufferDebugState<C>,
|
||||
|
|
@ -990,18 +998,20 @@ pub struct RenameExecuteRetireDebugState<C: PhantomConstGet<CpuConfig>> {
|
|||
ArrayType<HdlOption<PRegValue>, CpuConfig2PowOutRegNumWidth<C>>,
|
||||
CpuConfigUnitCount<C>,
|
||||
>,
|
||||
lfsr: LFSR31,
|
||||
per_insn_timeline: ArrayType<SimOnlyString, PerInsnTimelineLen<C>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RenameExecuteRetireState<C: PhantomConstCpuConfig> {
|
||||
rename_delayed: VecDeque<SimValue<MOpInstance<MOp>>>,
|
||||
rename_delayed: VecDeque<SimValue<RenameDelayedEntry>>,
|
||||
rename_table: RenameTable<C>,
|
||||
retire_rename_table: RenameTable<C>,
|
||||
rob: ReorderBuffer<C>,
|
||||
next_pc_canceling: Option<NextPcCancelingState>,
|
||||
unit_canceling: Box<[bool]>,
|
||||
l1_reg_file: Box<[Box<[Option<SimValue<PRegValue>>]>]>,
|
||||
lfsr: SimValue<LFSR31>,
|
||||
l2_reg_file_unit_index: usize,
|
||||
config: C,
|
||||
}
|
||||
|
|
@ -1020,6 +1030,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
CpuConfigUnitCount[config]
|
||||
]
|
||||
.into_boxed_slice(),
|
||||
lfsr: LFSR31::new(),
|
||||
l2_reg_file_unit_index: config
|
||||
.get()
|
||||
.units
|
||||
|
|
@ -1079,16 +1090,17 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
state_for_debug: Expr<RenameExecuteRetireDebugState<C>>,
|
||||
) {
|
||||
let Self {
|
||||
ref rename_delayed,
|
||||
ref rename_table,
|
||||
ref retire_rename_table,
|
||||
ref rob,
|
||||
ref next_pc_canceling,
|
||||
ref unit_canceling,
|
||||
ref l1_reg_file,
|
||||
rename_delayed,
|
||||
rename_table,
|
||||
retire_rename_table,
|
||||
rob,
|
||||
next_pc_canceling,
|
||||
unit_canceling,
|
||||
l1_reg_file,
|
||||
lfsr,
|
||||
l2_reg_file_unit_index: _,
|
||||
config: _,
|
||||
} = *self;
|
||||
} = self;
|
||||
sim.write(
|
||||
state_for_debug,
|
||||
#[hdl(sim)]
|
||||
|
|
@ -1109,6 +1121,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
SimValue::from_array_elements(state_for_debug.ty().l1_reg_file.element(), v)
|
||||
}),
|
||||
),
|
||||
lfsr,
|
||||
per_insn_timeline: self.per_insn_timeline(),
|
||||
},
|
||||
)
|
||||
|
|
@ -1133,7 +1146,10 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
.HdlSome
|
||||
.from_iter_sim(
|
||||
zeroed(MOpInstance[MOp]),
|
||||
self.rename_delayed.iter().chain(self.rob.unrenamed()),
|
||||
self.rename_delayed
|
||||
.iter()
|
||||
.map(|v| &v.mop)
|
||||
.chain(self.rob.unrenamed()),
|
||||
)
|
||||
.ok()
|
||||
.expect("known to fit"),
|
||||
|
|
@ -1242,16 +1258,33 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
#[hdl]
|
||||
fn try_rename(
|
||||
&mut self,
|
||||
insn: SimValue<MOpInstance<MOp>>,
|
||||
) -> Result<(), SimValue<MOpInstance<MOp>>> {
|
||||
entry: SimValue<RenameDelayedEntry>,
|
||||
) -> Result<(), SimValue<RenameDelayedEntry>> {
|
||||
#[hdl(sim)]
|
||||
let RenameDelayedEntry {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
} = entry;
|
||||
println!("try_rename: insn: {insn:?}");
|
||||
if self.rob.unrenamed_len() >= self.config.get().rob_size.get() {
|
||||
println!("try_rename: unrenamed_len >= rob_size");
|
||||
return Err(insn);
|
||||
return Err(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
},
|
||||
);
|
||||
}
|
||||
if self.rob.renamed_len() >= self.config.get().rob_size.get() {
|
||||
println!("try_rename: renamed_len >= rob_size");
|
||||
return Err(insn);
|
||||
return Err(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
},
|
||||
);
|
||||
}
|
||||
let unit_kind = UnitMOp::kind_sim(&insn.mop);
|
||||
#[hdl(sim)]
|
||||
|
|
@ -1327,16 +1360,29 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
};
|
||||
if space_available == 0 {
|
||||
println!("try_rename: space_available = 0");
|
||||
return Err(insn);
|
||||
return Err(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
},
|
||||
);
|
||||
}
|
||||
let Some(out_reg_num) = out_reg_num else {
|
||||
println!("try_rename: out_reg_num = None");
|
||||
return if self.space_available_for_unit(self.l2_reg_file_unit_index) > 0
|
||||
return if !*started_l2_store
|
||||
&& self.space_available_for_unit(self.l2_reg_file_unit_index) > 0
|
||||
&& let Some(l2_reg_index) = self.find_free_l2_reg()
|
||||
{
|
||||
todo!("maybe start a L2 register file store");
|
||||
} else {
|
||||
Err(insn)
|
||||
Err(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
},
|
||||
)
|
||||
};
|
||||
};
|
||||
let out_reg_num_sim = UnitOutRegNum[self.config].new_sim(out_reg_num);
|
||||
|
|
@ -1414,7 +1460,13 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
Ok(())
|
||||
} else {
|
||||
println!("try_rename: l2 reg file has no space and/or has no free output regs");
|
||||
Err(insn)
|
||||
Err(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
},
|
||||
)
|
||||
};
|
||||
}
|
||||
let mop = UnitMOp::with_transformed_move_op_sim(
|
||||
|
|
@ -1641,13 +1693,20 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
)
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn handle_from_post_decode(&mut self, insns: &[SimValue<MOpInstance<MOp>>]) {
|
||||
if self.is_canceling() {
|
||||
assert!(insns.is_empty());
|
||||
return;
|
||||
}
|
||||
for insn in insns {
|
||||
self.rename_delayed.push_back(insn.clone());
|
||||
self.rename_delayed.push_back(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
mop: insn,
|
||||
started_l2_store: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
for _ in 0..CpuConfigFetchWidth[self.config] {
|
||||
let Some(insn) = self.rename_delayed.pop_front() else {
|
||||
|
|
@ -1672,6 +1731,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
next_pc_canceling,
|
||||
unit_canceling: _,
|
||||
l1_reg_file: _,
|
||||
lfsr: _,
|
||||
l2_reg_file_unit_index: _,
|
||||
config: _,
|
||||
} = self;
|
||||
|
|
|
|||
|
|
@ -280,3 +280,33 @@ impl<T: Type, N: Size> Rotate<usize> for SimValue<ArrayType<T, N>> {
|
|||
retval
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct LFSR31 {
|
||||
// MSB is always zero, 32 bits makes it easier to manipulate
|
||||
state: UInt<32>,
|
||||
}
|
||||
|
||||
impl LFSR31 {
|
||||
#[hdl]
|
||||
pub fn new() -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
Self { state: 1u32 }
|
||||
}
|
||||
}
|
||||
|
||||
impl LFSR31 {
|
||||
pub fn next_sim(this: &mut SimValue<Self>) -> u32 {
|
||||
let state = this.state.as_int();
|
||||
let state = if state == 0 {
|
||||
1u32
|
||||
} else {
|
||||
// a maximal-length 31-bit LFSR
|
||||
let lsb = ((state >> 30) ^ (state >> 27)) & 1;
|
||||
let msb = (state << 1) & ((1 << 31) - 1);
|
||||
lsb | msb
|
||||
};
|
||||
*this.state = state.into();
|
||||
state
|
||||
}
|
||||
}
|
||||
|
|
|
|||
270063
crates/cpu/tests/expected/rename_execute_retire_slow_loop.vcd
generated
270063
crates/cpu/tests/expected/rename_execute_retire_slow_loop.vcd
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,9 +8,10 @@ use cpu::{
|
|||
},
|
||||
instruction::{
|
||||
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
|
||||
CompareMOp, CompareMode, ConditionMode, L2RegisterFileMOp, LoadMOp, LoadStoreCommonMOp,
|
||||
LoadStoreConversion, LoadStoreMOp, LoadStoreWidth, MOp, MOpDestReg, MOpRegNum, MOpTrait,
|
||||
MoveRegMOp, OutputIntegerMode, PRegNum, StoreMOp, UnitNum,
|
||||
CompareMOp, CompareMode, ConditionMode, L2RegNum, L2RegisterFileMOp, LoadMOp,
|
||||
LoadStoreCommonMOp, LoadStoreConversion, LoadStoreMOp, LoadStoreWidth, MOp, MOpDestReg,
|
||||
MOpRegNum, MOpTrait, MoveRegMOp, OutputIntegerMode, PRegNum, ReadL2RegMOp, StoreMOp,
|
||||
UnitNum, WriteL2RegMOp,
|
||||
},
|
||||
next_pc::CallStackOp,
|
||||
register::{PRegFlags, PRegFlagsPowerISA, PRegValue},
|
||||
|
|
@ -870,6 +871,7 @@ fn mock_next_pc<#[hdl(skip)] MI: MakeInsns>(config: PhantomConst<CpuConfig>) {
|
|||
state: Cell::new(0),
|
||||
};
|
||||
let insns = MI::make_insns();
|
||||
dbg!(&insns);
|
||||
sim.resettable(
|
||||
cd,
|
||||
async |mut sim| {
|
||||
|
|
@ -1401,19 +1403,8 @@ impl MockMemory {
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct MockL2RegFileDebugState {}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct MockL2RegFile {}
|
||||
|
||||
trait MockExecutionStateTrait: Default {
|
||||
type DebugState: BundleType;
|
||||
fn try_get_l2_reg_file(&mut self) -> Option<&mut MockL2RegFile>;
|
||||
fn get_l2_reg_file(&mut self) -> &mut MockL2RegFile {
|
||||
self.try_get_l2_reg_file()
|
||||
.expect("no MockL2RegFile available in this unit")
|
||||
}
|
||||
fn debug_state_ty() -> Self::DebugState;
|
||||
fn zeroed_debug_state() -> SimValue<Self::DebugState>;
|
||||
fn debug_state(&self) -> SimValue<Self::DebugState>;
|
||||
|
|
@ -1764,46 +1755,17 @@ trait MockExecutionStateTrait: Default {
|
|||
);
|
||||
}
|
||||
UnitMOp::<_, _, _>::TransformedMove(mop) => {
|
||||
let l2_reg_file = self.get_l2_reg_file();
|
||||
#[hdl(sim)]
|
||||
match mop {
|
||||
L2RegisterFileMOp::<_, _>::ReadL2Reg(mop) => {
|
||||
todo!("implement ReadL2Reg")
|
||||
}
|
||||
L2RegisterFileMOp::<_, _>::WriteL2Reg(mop) => {
|
||||
todo!("implement WriteL2Reg")
|
||||
}
|
||||
}
|
||||
panic!(
|
||||
"mock_unit can't execute L2RegisterFile MOp, that needs mock_l2_reg_file_unit: {mop:#?}"
|
||||
);
|
||||
}
|
||||
UnitMOp::<_, _, _>::Unknown => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MockExecutionStateTrait for MockL2RegFile {
|
||||
type DebugState = MockL2RegFileDebugState;
|
||||
fn try_get_l2_reg_file(&mut self) -> Option<&mut MockL2RegFile> {
|
||||
Some(self)
|
||||
}
|
||||
fn debug_state_ty() -> Self::DebugState {
|
||||
MockL2RegFileDebugState
|
||||
}
|
||||
fn zeroed_debug_state() -> SimValue<Self::DebugState> {
|
||||
zeroed(Self::debug_state_ty())
|
||||
}
|
||||
#[hdl]
|
||||
fn debug_state(&self) -> SimValue<Self::DebugState> {
|
||||
let Self {} = self;
|
||||
#[hdl(sim)]
|
||||
MockL2RegFileDebugState {}
|
||||
}
|
||||
}
|
||||
|
||||
impl MockExecutionStateTrait for () {
|
||||
type DebugState = ();
|
||||
fn try_get_l2_reg_file(&mut self) -> Option<&mut MockL2RegFile> {
|
||||
None
|
||||
}
|
||||
fn debug_state_ty() -> Self::DebugState {
|
||||
()
|
||||
}
|
||||
|
|
@ -2260,6 +2222,542 @@ fn mock_unit<#[hdl(skip)] E: MockExecutionStateTrait>(
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct MockL2RegFileOpDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
mop: MOpInstance<L2RegisterFileMOp<PRegNum<C>, PRegNum<C>>>,
|
||||
src_values: HdlOption<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||
dest_value: HdlOption<PRegValue>,
|
||||
sent_cant_cause_cancel: Bool,
|
||||
sent_output_ready: Bool,
|
||||
config: C,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockL2RegFileOp<C: PhantomConstCpuConfig> {
|
||||
mop: SimValue<MOpInstance<L2RegisterFileMOp<PRegNum<C>, PRegNum<C>>>>,
|
||||
src_values: Option<[SimValue<PRegValue>; COMMON_MOP_SRC_LEN]>,
|
||||
dest_value: Option<SimValue<PRegValue>>,
|
||||
sent_cant_cause_cancel: bool,
|
||||
sent_output_ready: bool,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> MockL2RegFileOp<C> {
|
||||
#[hdl]
|
||||
fn debug_state(&self) -> SimValue<MockL2RegFileOpDebugState<C>> {
|
||||
let Self {
|
||||
mop,
|
||||
src_values,
|
||||
dest_value,
|
||||
sent_cant_cause_cancel,
|
||||
sent_output_ready,
|
||||
config,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
MockL2RegFileOpDebugState::<_> {
|
||||
mop,
|
||||
src_values,
|
||||
dest_value,
|
||||
sent_cant_cause_cancel,
|
||||
sent_output_ready,
|
||||
config,
|
||||
}
|
||||
}
|
||||
fn id(&self) -> &SimValue<MOpId> {
|
||||
&self.mop.id
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(get(|_| L2RegNum.l2_reg_count()))]
|
||||
type L2RegFileSize<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct MockL2RegFileUnitDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
ops: ArrayVec<MockL2RegFileOpDebugState<C>, CpuConfigMaxUnitMaxInFlight<C>>,
|
||||
l2_regs: ArrayType<PRegValue, L2RegFileSize<C>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
struct MockL2RegFileUnitState<C: PhantomConstCpuConfig> {
|
||||
ops: VecDeque<MockL2RegFileOp<C>>,
|
||||
l2_regs: Box<[SimValue<PRegValue>]>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> MockL2RegFileUnitState<C> {
|
||||
fn new(config: C) -> Self {
|
||||
Self {
|
||||
ops: VecDeque::new(),
|
||||
l2_regs: vec![PRegValue::zeroed_sim(); L2RegFileSize[config]].into_boxed_slice(),
|
||||
config,
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn debug_state(&self) -> SimValue<MockL2RegFileUnitDebugState<C>> {
|
||||
let Self {
|
||||
ops,
|
||||
l2_regs,
|
||||
config,
|
||||
} = self;
|
||||
let ret_ty = MockL2RegFileUnitDebugState[*config];
|
||||
#[hdl(sim)]
|
||||
MockL2RegFileUnitDebugState::<_> {
|
||||
ops: ret_ty
|
||||
.ops
|
||||
.from_iter_sim(
|
||||
zeroed(ret_ty.ops.element()),
|
||||
ops.iter().map(MockL2RegFileOp::debug_state),
|
||||
)
|
||||
.ok()
|
||||
.expect("known to fit"),
|
||||
l2_regs,
|
||||
config,
|
||||
}
|
||||
}
|
||||
fn try_op_by_id(&self, id: &SimValue<MOpId>) -> Option<&MockL2RegFileOp<C>> {
|
||||
self.ops.iter().find(|op| op.id() == id)
|
||||
}
|
||||
fn try_op_by_id_mut(&mut self, id: &SimValue<MOpId>) -> Option<&mut MockL2RegFileOp<C>> {
|
||||
self.ops.iter_mut().find(|op| op.id() == id)
|
||||
}
|
||||
#[track_caller]
|
||||
fn op_by_id(&self, id: &SimValue<MOpId>) -> &MockL2RegFileOp<C> {
|
||||
match self.try_op_by_id(id) {
|
||||
Some(retval) => retval,
|
||||
None => panic!("can't find MockL2RegFileOp with id: {id:?}"),
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
fn op_by_id_mut(&mut self, id: &SimValue<MOpId>) -> &mut MockL2RegFileOp<C> {
|
||||
match self.try_op_by_id_mut(id) {
|
||||
Some(retval) => retval,
|
||||
None => panic!("can't find MockL2RegFileOp with id: {id:?}"),
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn step(&mut self) {
|
||||
for op in &mut self.ops {
|
||||
let MockL2RegFileOp {
|
||||
mop,
|
||||
src_values,
|
||||
dest_value,
|
||||
sent_cant_cause_cancel: _,
|
||||
sent_output_ready: _,
|
||||
config: _,
|
||||
} = op;
|
||||
#[hdl(sim)]
|
||||
match &mop.mop {
|
||||
L2RegisterFileMOp::<_, _>::ReadL2Reg(mop) => {
|
||||
#[hdl(sim)]
|
||||
let ReadL2RegMOp::<_, _> { common } = mop;
|
||||
if src_values.is_some() && dest_value.is_none() {
|
||||
*dest_value = Some(self.l2_regs[L2RegNum::value_sim(&common.imm)].clone());
|
||||
}
|
||||
}
|
||||
L2RegisterFileMOp::<_, _>::WriteL2Reg(mop) => {
|
||||
#[hdl(sim)]
|
||||
let WriteL2RegMOp::<_, _> { common } = mop;
|
||||
if let Some(src_values) = src_values
|
||||
&& dest_value.is_none()
|
||||
{
|
||||
self.l2_regs[L2RegNum::value_sim(&common.imm)] = src_values[0].clone();
|
||||
*dest_value = Some(PRegValue::zeroed_sim());
|
||||
}
|
||||
if dest_value.is_none() {
|
||||
// we can't run following reads yet.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn handle_enqueue(&mut self, enqueue: SimValue<UnitEnqueue<C>>) {
|
||||
#[hdl(sim)]
|
||||
let UnitEnqueue::<_> { mop, config: _ } = enqueue;
|
||||
#[hdl(sim)]
|
||||
let MOpInstance::<_> {
|
||||
fetch_block_id,
|
||||
id,
|
||||
pc,
|
||||
predicted_next_pc,
|
||||
size_in_bytes,
|
||||
is_first_mop_in_insn,
|
||||
is_last_mop_in_insn,
|
||||
mop,
|
||||
} = mop;
|
||||
let mop = #[hdl(sim)]
|
||||
match &mop {
|
||||
RenamedMOp::<_>::TransformedMove(mop) => mop,
|
||||
_ => {
|
||||
panic!("MockL2RegFileUnitState can only handle L2RegisterFile MOps, got: {mop:#?}");
|
||||
}
|
||||
};
|
||||
let mop = #[hdl(sim)]
|
||||
MOpInstance::<_> {
|
||||
fetch_block_id,
|
||||
id,
|
||||
pc,
|
||||
predicted_next_pc,
|
||||
size_in_bytes,
|
||||
is_first_mop_in_insn,
|
||||
is_last_mop_in_insn,
|
||||
mop,
|
||||
};
|
||||
self.ops.push_back(MockL2RegFileOp {
|
||||
mop,
|
||||
src_values: None,
|
||||
dest_value: None,
|
||||
sent_cant_cause_cancel: false,
|
||||
sent_output_ready: false,
|
||||
config: self.config,
|
||||
});
|
||||
}
|
||||
#[hdl]
|
||||
fn handle_inputs_ready(&mut self, inputs_ready: SimValue<UnitInputsReady<C>>) {
|
||||
#[hdl(sim)]
|
||||
let UnitInputsReady::<_> {
|
||||
mop,
|
||||
config: _,
|
||||
src_values,
|
||||
} = inputs_ready;
|
||||
let MockL2RegFileOp {
|
||||
mop: _,
|
||||
src_values: op_src_values,
|
||||
dest_value: _,
|
||||
sent_cant_cause_cancel: _,
|
||||
sent_output_ready: _,
|
||||
config: _,
|
||||
} = self.op_by_id_mut(&mop.id);
|
||||
assert!(op_src_values.is_none());
|
||||
*op_src_values = Some(SimValue::into_value(src_values));
|
||||
}
|
||||
#[hdl]
|
||||
fn handle_mop_is_no_longer_speculative(
|
||||
&mut self,
|
||||
is_no_longer_speculative: SimValue<UnitMOpIsNoLongerSpeculative<C>>,
|
||||
) {
|
||||
#[hdl(sim)]
|
||||
let UnitMOpIsNoLongerSpeculative::<_> { id, config: _ } = is_no_longer_speculative;
|
||||
let MockL2RegFileOp {
|
||||
mop: _,
|
||||
src_values: _,
|
||||
dest_value: _,
|
||||
sent_cant_cause_cancel: _,
|
||||
sent_output_ready: _,
|
||||
config: _,
|
||||
} = self.op_by_id_mut(&id);
|
||||
}
|
||||
#[hdl]
|
||||
fn peek_mop_cant_cause_cancel(&self) -> SimValue<HdlOption<UnitMOpCantCauseCancel<C>>> {
|
||||
let ret_ty = HdlOption[UnitMOpCantCauseCancel[self.config]];
|
||||
for MockL2RegFileOp {
|
||||
mop,
|
||||
src_values: _,
|
||||
dest_value: _,
|
||||
sent_cant_cause_cancel,
|
||||
sent_output_ready: _,
|
||||
config: _,
|
||||
} in &self.ops
|
||||
{
|
||||
if *sent_cant_cause_cancel {
|
||||
continue;
|
||||
}
|
||||
// L2 reg file ops can never cause cancels
|
||||
return #[hdl(sim)]
|
||||
ret_ty.HdlSome(
|
||||
#[hdl(sim)]
|
||||
UnitMOpCantCauseCancel::<_> {
|
||||
id: &mop.id,
|
||||
config: self.config,
|
||||
},
|
||||
);
|
||||
}
|
||||
#[hdl(sim)]
|
||||
ret_ty.HdlNone()
|
||||
}
|
||||
#[hdl]
|
||||
fn handle_mop_cant_cause_cancel(
|
||||
&mut self,
|
||||
cant_cause_cancel: SimValue<UnitMOpCantCauseCancel<C>>,
|
||||
) {
|
||||
#[hdl(sim)]
|
||||
let UnitMOpCantCauseCancel::<_> { id, config: _ } = cant_cause_cancel;
|
||||
let op = self.op_by_id_mut(&id);
|
||||
assert!(!op.sent_cant_cause_cancel);
|
||||
op.sent_cant_cause_cancel = true;
|
||||
}
|
||||
#[hdl]
|
||||
fn peek_output_ready(&self) -> SimValue<HdlOption<UnitOutputReady<C>>> {
|
||||
let ret_ty = HdlOption[UnitOutputReady[self.config]];
|
||||
for MockL2RegFileOp {
|
||||
mop,
|
||||
src_values: _,
|
||||
dest_value,
|
||||
sent_cant_cause_cancel: _,
|
||||
sent_output_ready,
|
||||
config: _,
|
||||
} in &self.ops
|
||||
{
|
||||
if *sent_output_ready {
|
||||
continue;
|
||||
}
|
||||
if let Some(dest_value) = dest_value {
|
||||
return #[hdl(sim)]
|
||||
ret_ty.HdlSome(
|
||||
#[hdl(sim)]
|
||||
UnitOutputReady::<_> {
|
||||
id: &mop.id,
|
||||
dest_value,
|
||||
predictor_op: #[hdl(sim)]
|
||||
NextPcPredictorOp::<_> {
|
||||
call_stack_op: #[hdl(sim)]
|
||||
CallStackOp.None(),
|
||||
cond_br_taken: #[hdl(sim)]
|
||||
HdlNone(),
|
||||
config: self.config,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
#[hdl(sim)]
|
||||
ret_ty.HdlNone()
|
||||
}
|
||||
#[hdl]
|
||||
fn handle_output_ready(&mut self, output_ready: SimValue<UnitOutputReady<C>>) {
|
||||
#[hdl(sim)]
|
||||
let UnitOutputReady::<_> {
|
||||
id,
|
||||
dest_value: _,
|
||||
predictor_op: _,
|
||||
} = output_ready;
|
||||
let op = self.op_by_id_mut(&id);
|
||||
assert!(!op.sent_output_ready);
|
||||
op.sent_output_ready = true;
|
||||
}
|
||||
#[hdl]
|
||||
fn peek_finish_cause_cancel(&self) -> SimValue<HdlOption<UnitFinishCauseCancel<C>>> {
|
||||
let ret_ty = HdlOption[UnitFinishCauseCancel[self.config]];
|
||||
if let Some(MockL2RegFileOp {
|
||||
mop,
|
||||
src_values: _,
|
||||
dest_value,
|
||||
sent_cant_cause_cancel: _,
|
||||
sent_output_ready: _,
|
||||
config: _,
|
||||
}) = self.ops.front()
|
||||
{
|
||||
if dest_value.is_some() {
|
||||
return #[hdl(sim)]
|
||||
ret_ty.HdlSome(
|
||||
#[hdl(sim)]
|
||||
UnitFinishCauseCancel::<_> {
|
||||
id: &mop.id,
|
||||
caused_cancel: #[hdl(sim)]
|
||||
(ret_ty.HdlSome.caused_cancel).HdlNone(),
|
||||
config: self.config,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
#[hdl(sim)]
|
||||
ret_ty.HdlNone()
|
||||
}
|
||||
#[hdl]
|
||||
fn handle_finish_cause_cancel(
|
||||
&mut self,
|
||||
finish_cause_cancel: SimValue<UnitFinishCauseCancel<C>>,
|
||||
) {
|
||||
#[hdl(sim)]
|
||||
let UnitFinishCauseCancel::<_> {
|
||||
id,
|
||||
caused_cancel: _,
|
||||
config: _,
|
||||
} = finish_cause_cancel;
|
||||
let Some(op) = self.ops.pop_front() else {
|
||||
unreachable!();
|
||||
};
|
||||
assert_eq!(*op.id(), id);
|
||||
}
|
||||
fn cancel_all(&mut self) {
|
||||
let Self {
|
||||
ops,
|
||||
l2_regs: _,
|
||||
config: _,
|
||||
} = self;
|
||||
ops.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(extern)]
|
||||
fn mock_l2_reg_file_unit(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
||||
assert!(
|
||||
config
|
||||
.get()
|
||||
.units
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, unit)| (unit_index == i) == (unit.kind == UnitKind::TransformedMove)),
|
||||
"unit index {unit_index} must be the only L2 register file unit: {config:#?}",
|
||||
);
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let from_execute: ExecuteToUnitInterface<PhantomConst<CpuConfig>> =
|
||||
m.input(ExecuteToUnitInterface[config]);
|
||||
#[hdl]
|
||||
let debug_state: MockL2RegFileUnitDebugState<PhantomConst<CpuConfig>> =
|
||||
m.output(MockL2RegFileUnitDebugState[config]);
|
||||
m.register_clock_for_past(cd.clk);
|
||||
m.extern_module_simulation_fn(
|
||||
(cd, from_execute, debug_state, config),
|
||||
async |args, mut sim| {
|
||||
let (cd, from_execute, debug_state, config) = args;
|
||||
sim.resettable(
|
||||
cd,
|
||||
async |mut sim| {
|
||||
#[hdl]
|
||||
let ExecuteToUnitInterface::<_> {
|
||||
enqueue,
|
||||
inputs_ready: _,
|
||||
is_no_longer_speculative: _,
|
||||
cant_cause_cancel,
|
||||
output_ready,
|
||||
finish_cause_cancel,
|
||||
unit_outputs_ready: _,
|
||||
cancel_all,
|
||||
config: _,
|
||||
} = from_execute;
|
||||
sim.write(enqueue.ready, false).await;
|
||||
sim.write(cancel_all.ready, false).await;
|
||||
sim.write(
|
||||
cant_cause_cancel,
|
||||
#[hdl(sim)]
|
||||
(cant_cause_cancel.ty()).HdlNone(),
|
||||
)
|
||||
.await;
|
||||
sim.write(
|
||||
output_ready,
|
||||
#[hdl(sim)]
|
||||
(output_ready.ty()).HdlNone(),
|
||||
)
|
||||
.await;
|
||||
sim.write(
|
||||
finish_cause_cancel,
|
||||
#[hdl(sim)]
|
||||
(finish_cause_cancel.ty()).HdlNone(),
|
||||
)
|
||||
.await;
|
||||
sim.write(
|
||||
debug_state,
|
||||
#[hdl(sim)]
|
||||
MockL2RegFileUnitDebugState::<_> {
|
||||
ops: zeroed(debug_state.ty().ops),
|
||||
l2_regs: zeroed(debug_state.ty().l2_regs),
|
||||
config,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
},
|
||||
|sim, ()| run_fn(cd, from_execute, debug_state, config, sim),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
);
|
||||
#[hdl]
|
||||
async fn run_fn(
|
||||
cd: Expr<ClockDomain>,
|
||||
from_execute: Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>>,
|
||||
debug_state: Expr<MockL2RegFileUnitDebugState<PhantomConst<CpuConfig>>>,
|
||||
config: PhantomConst<CpuConfig>,
|
||||
mut sim: ExternModuleSimulationState,
|
||||
) {
|
||||
let mut state = MockL2RegFileUnitState::new(config);
|
||||
loop {
|
||||
{
|
||||
#[hdl]
|
||||
let ExecuteToUnitInterface::<_> {
|
||||
enqueue,
|
||||
inputs_ready: _,
|
||||
is_no_longer_speculative: _,
|
||||
cant_cause_cancel,
|
||||
output_ready,
|
||||
finish_cause_cancel,
|
||||
unit_outputs_ready: _,
|
||||
cancel_all,
|
||||
config: _,
|
||||
} = from_execute;
|
||||
sim.write(enqueue.ready, true).await;
|
||||
sim.write(cancel_all.ready, true).await;
|
||||
sim.write(cant_cause_cancel, state.peek_mop_cant_cause_cancel())
|
||||
.await;
|
||||
sim.write(output_ready, state.peek_output_ready()).await;
|
||||
sim.write(finish_cause_cancel, state.peek_finish_cause_cancel())
|
||||
.await;
|
||||
}
|
||||
sim.write(debug_state, state.debug_state()).await;
|
||||
sim.wait_for_clock_edge(cd.clk).await;
|
||||
{
|
||||
#[hdl]
|
||||
let ExecuteToUnitInterface::<_> {
|
||||
enqueue,
|
||||
inputs_ready,
|
||||
is_no_longer_speculative,
|
||||
cant_cause_cancel,
|
||||
output_ready,
|
||||
finish_cause_cancel,
|
||||
unit_outputs_ready,
|
||||
cancel_all,
|
||||
config: _,
|
||||
} = from_execute;
|
||||
if sim.read_past_bool(enqueue.ready, cd.clk).await {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(enqueue) = sim.read_past(enqueue.data, cd.clk).await {
|
||||
state.handle_enqueue(enqueue);
|
||||
}
|
||||
}
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(inputs_ready) = sim.read_past(inputs_ready, cd.clk).await {
|
||||
state.handle_inputs_ready(inputs_ready);
|
||||
}
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(is_no_longer_speculative) =
|
||||
sim.read_past(is_no_longer_speculative, cd.clk).await
|
||||
{
|
||||
state.handle_mop_is_no_longer_speculative(is_no_longer_speculative);
|
||||
}
|
||||
if sim.read_past_bool(unit_outputs_ready, cd.clk).await {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(cant_cause_cancel) =
|
||||
sim.read_past(cant_cause_cancel, cd.clk).await
|
||||
{
|
||||
state.handle_mop_cant_cause_cancel(cant_cause_cancel);
|
||||
}
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(output_ready) = sim.read_past(output_ready, cd.clk).await {
|
||||
state.handle_output_ready(output_ready);
|
||||
}
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(finish_cause_cancel) =
|
||||
sim.read_past(finish_cause_cancel, cd.clk).await
|
||||
{
|
||||
state.handle_finish_cause_cancel(finish_cause_cancel);
|
||||
}
|
||||
}
|
||||
if sim.read_past_bool(cancel_all.ready, cd.clk).await {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(cancel_all) = sim.read_past(cancel_all.data, cd.clk).await {
|
||||
let () = *cancel_all;
|
||||
state.cancel_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
state.step();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct MockLoadStoreOpDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
mop: MOpInstance<LoadStoreMOp<PRegNum<C>, PRegNum<C>>>,
|
||||
|
|
@ -2929,31 +3427,38 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: Phanto
|
|||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
if config.get().units[unit_index].kind == UnitKind::LoadStore {
|
||||
let mock_unit = instance_with_loc(
|
||||
&dut.ty().to_units.unit_field_name(unit_index),
|
||||
mock_load_store_unit::<MI>(config, unit_index),
|
||||
SourceLocation::caller(),
|
||||
);
|
||||
connect(mock_unit.cd, cd);
|
||||
connect(mock_unit.from_execute, to_unit);
|
||||
#[hdl]
|
||||
if !mock_unit.all_outputs_written {
|
||||
connect(all_outputs_written, false);
|
||||
match config.get().units[unit_index].kind {
|
||||
UnitKind::LoadStore => {
|
||||
let mock_unit = instance_with_loc(
|
||||
&dut.ty().to_units.unit_field_name(unit_index),
|
||||
mock_load_store_unit::<MI>(config, unit_index),
|
||||
SourceLocation::caller(),
|
||||
);
|
||||
connect(mock_unit.cd, cd);
|
||||
connect(mock_unit.from_execute, to_unit);
|
||||
#[hdl]
|
||||
if !mock_unit.all_outputs_written {
|
||||
connect(all_outputs_written, false);
|
||||
}
|
||||
}
|
||||
UnitKind::TransformedMove => {
|
||||
let mock_unit = instance_with_loc(
|
||||
&dut.ty().to_units.unit_field_name(unit_index),
|
||||
mock_l2_reg_file_unit(config, unit_index),
|
||||
SourceLocation::caller(),
|
||||
);
|
||||
connect(mock_unit.cd, cd);
|
||||
connect(mock_unit.from_execute, to_unit);
|
||||
}
|
||||
UnitKind::AluBranch => {
|
||||
let mock_unit = instance_with_loc(
|
||||
&dut.ty().to_units.unit_field_name(unit_index),
|
||||
mock_unit::<()>(config, unit_index),
|
||||
SourceLocation::caller(),
|
||||
);
|
||||
connect(mock_unit.cd, cd);
|
||||
connect(mock_unit.from_execute, to_unit);
|
||||
}
|
||||
} else {
|
||||
let mock_unit_module = if is_the_l2_reg_file_unit(config, unit_index) {
|
||||
mock_unit::<MockL2RegFile>(config, unit_index)
|
||||
} else {
|
||||
mock_unit::<()>(config, unit_index)
|
||||
};
|
||||
let mock_unit = instance_with_loc(
|
||||
&dut.ty().to_units.unit_field_name(unit_index),
|
||||
mock_unit_module,
|
||||
SourceLocation::caller(),
|
||||
);
|
||||
connect(mock_unit.cd, cd);
|
||||
connect(mock_unit.from_execute, to_unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3014,7 +3519,7 @@ struct SlowLoopInsns;
|
|||
|
||||
impl SlowLoopInsns {
|
||||
const CONSTANTS_ADDR: u64 = 0x4000;
|
||||
const CONSTANTS_COUNT: usize = 5;
|
||||
const CONSTANTS_COUNT: usize = 16;
|
||||
const CONSTANTS_STEP: usize = 8;
|
||||
const LOG2_RESULT_FACTOR: u32 = 2;
|
||||
}
|
||||
|
|
@ -3033,9 +3538,9 @@ impl MakeInsns for SlowLoopInsns {
|
|||
|
||||
b.set_pc(0x1000);
|
||||
b.define_label(slow_loop);
|
||||
b.power_isa_addi(4, 0, 0); // clear sum
|
||||
|
||||
let loop_header = b.new_defined_label("loop_header");
|
||||
b.power_isa_addi(4, 0, 0); // clear sum
|
||||
b.power_isa_cmpldi(0, 3, 0);
|
||||
let loop_done = b.new_label("loop_done");
|
||||
b.power_isa_beq(loop_done); // if input == 0 goto loop_done
|
||||
|
|
@ -3053,7 +3558,7 @@ impl MakeInsns for SlowLoopInsns {
|
|||
(Self::CONSTANTS_ADDR + (Self::CONSTANTS_STEP * i) as u64) as i16,
|
||||
);
|
||||
}
|
||||
for i in 1..Self::CONSTANTS_COUNT {
|
||||
for i in 0..Self::CONSTANTS_COUNT {
|
||||
b.power_isa_add(4, 4, start_reg + i);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue