rename_execute_retire: implement generating L2 reg file writes
All checks were successful
/ test (pull_request) Successful in 12m13s
All checks were successful
/ test (pull_request) Successful in 12m13s
This commit is contained in:
parent
33b5d59507
commit
3fbdab0862
6 changed files with 867356 additions and 13009 deletions
|
|
@ -20,7 +20,7 @@ use crate::{
|
||||||
CpuConfig, CpuConfigFetchWidth, CpuConfigMaxFetchesInFlight, CpuConfigRobSize,
|
CpuConfig, CpuConfigFetchWidth, CpuConfigMaxFetchesInFlight, CpuConfigRobSize,
|
||||||
PhantomConstCpuConfig, TwiceCpuConfigFetchWidth,
|
PhantomConstCpuConfig, TwiceCpuConfigFetchWidth,
|
||||||
},
|
},
|
||||||
util::array_vec::ArrayVec,
|
util::{LFSR31, array_vec::ArrayVec},
|
||||||
};
|
};
|
||||||
use fayalite::{
|
use fayalite::{
|
||||||
int::{UIntInRange, UIntInRangeInclusive, UIntInRangeInclusiveType, UIntInRangeType},
|
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 {
|
impl SimValueDefault for LFSR31 {
|
||||||
#[hdl]
|
|
||||||
fn sim_value_default(self) -> SimValue<Self> {
|
fn sim_value_default(self) -> SimValue<Self> {
|
||||||
#[hdl(sim)]
|
Self::new()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2995,7 +2971,7 @@ impl BranchTargetBuffer {
|
||||||
const LOG2_SIZE: usize = 4;
|
const LOG2_SIZE: usize = 4;
|
||||||
const SIZE: usize = 1 << Self::LOG2_SIZE;
|
const SIZE: usize = 1 << Self::LOG2_SIZE;
|
||||||
fn next_index_to_replace(this: &mut SimValue<Self>) -> usize {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,21 +11,25 @@ use crate::{
|
||||||
},
|
},
|
||||||
instruction::{
|
instruction::{
|
||||||
COMMON_MOP_SRC_LEN, L2RegNum, L2RegisterFileMOp, MOp, MOpDestReg, MOpRegNum, MOpTrait,
|
COMMON_MOP_SRC_LEN, L2RegNum, L2RegisterFileMOp, MOp, MOpDestReg, MOpRegNum, MOpTrait,
|
||||||
PRegNum, ReadL2RegMOp, UnitNum, UnitOutRegNum,
|
PRegNum, ReadL2RegMOp, UnitNum, UnitOutRegNum, WriteL2RegMOp,
|
||||||
},
|
},
|
||||||
next_pc::{CallStackOp, SimValueDefault},
|
next_pc::{CallStackOp, SimValueDefault},
|
||||||
register::PRegValue,
|
register::PRegValue,
|
||||||
rename_execute_retire::to_unit_interfaces::ExecuteToUnitInterfaces,
|
rename_execute_retire::to_unit_interfaces::ExecuteToUnitInterfaces,
|
||||||
unit::{UnitKind, UnitMOp},
|
unit::{UnitKind, UnitMOp},
|
||||||
util::array_vec::ArrayVec,
|
util::{LFSR31, array_vec::ArrayVec},
|
||||||
};
|
};
|
||||||
use fayalite::{
|
use fayalite::{
|
||||||
int::UIntInRangeInclusiveType,
|
int::{UIntInRangeInclusiveType, UIntInRangeType},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
ty::{OpaqueSimValue, SimValueDebug, StaticType},
|
ty::{OpaqueSimValue, SimValueDebug, StaticType},
|
||||||
util::ready_valid::ReadyValid,
|
util::ready_valid::ReadyValid,
|
||||||
};
|
};
|
||||||
use std::{collections::VecDeque, fmt, mem, num::NonZero};
|
use std::{
|
||||||
|
collections::{BTreeSet, VecDeque},
|
||||||
|
fmt, mem,
|
||||||
|
num::NonZero,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod to_unit_interfaces;
|
pub mod to_unit_interfaces;
|
||||||
|
|
||||||
|
|
@ -227,6 +231,7 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RenameExecuteRetireDebugState
|
||||||
next_pc_canceling,
|
next_pc_canceling,
|
||||||
unit_canceling,
|
unit_canceling,
|
||||||
l1_reg_file,
|
l1_reg_file,
|
||||||
|
lfsr: _,
|
||||||
per_insn_timeline,
|
per_insn_timeline,
|
||||||
} = self;
|
} = self;
|
||||||
let empty_string = SimOnlyValue::new(String::new());
|
let empty_string = SimOnlyValue::new(String::new());
|
||||||
|
|
@ -239,6 +244,7 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RenameExecuteRetireDebugState
|
||||||
next_pc_canceling: zeroed(next_pc_canceling),
|
next_pc_canceling: zeroed(next_pc_canceling),
|
||||||
unit_canceling: zeroed(unit_canceling),
|
unit_canceling: zeroed(unit_canceling),
|
||||||
l1_reg_file: zeroed(l1_reg_file),
|
l1_reg_file: zeroed(l1_reg_file),
|
||||||
|
lfsr: LFSR31::new(),
|
||||||
per_insn_timeline: SimValue::from_array_elements(
|
per_insn_timeline: SimValue::from_array_elements(
|
||||||
per_insn_timeline,
|
per_insn_timeline,
|
||||||
(0..per_insn_timeline.len()).map(|_| empty_string.clone()),
|
(0..per_insn_timeline.len()).map(|_| empty_string.clone()),
|
||||||
|
|
@ -379,6 +385,22 @@ impl<C: PhantomConstCpuConfig> RenameTable<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[hdl]
|
||||||
|
fn used_unit_out_reg_count(&self, unit_index: usize) -> usize {
|
||||||
|
let mut seen = BTreeSet::new();
|
||||||
|
for entry in self.entries.iter() {
|
||||||
|
#[hdl(sim)]
|
||||||
|
match entry {
|
||||||
|
RenameTableEntry::<_>::L1(v) => {
|
||||||
|
if UnitNum::index_sim(&v.unit_num) == Some(unit_index) {
|
||||||
|
seen.insert(UnitOutRegNum::value_sim(&v.unit_out_reg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RenameTableEntry::<_>::L2(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seen.len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default)]
|
||||||
|
|
@ -568,9 +590,11 @@ type SimOnlyMOpInUnitState = SimOnly<MOpInUnitState>;
|
||||||
#[hdl(no_static)]
|
#[hdl(no_static)]
|
||||||
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
|
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||||
mop: MOpInstance<RenamedMOp<C>>,
|
mop: MOpInstance<RenamedMOp<C>>,
|
||||||
|
unit_index: UIntInRangeType<ConstUsize<0>, CpuConfigUnitCount<C>>,
|
||||||
mop_in_unit_state: SimOnlyMOpInUnitState,
|
mop_in_unit_state: SimOnlyMOpInUnitState,
|
||||||
is_speculative: Bool,
|
is_speculative: Bool,
|
||||||
finished: HdlOption<NextPcPredictorOp<C>>,
|
all_prior_mops_are_finished: Bool,
|
||||||
|
output: HdlOption<NextPcPredictorOp<C>>,
|
||||||
caused_cancel: HdlOption<UnitCausedCancel<C>>,
|
caused_cancel: HdlOption<UnitCausedCancel<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -579,18 +603,22 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
|
||||||
fn sim_value_default(self) -> SimValue<Self> {
|
fn sim_value_default(self) -> SimValue<Self> {
|
||||||
let Self {
|
let Self {
|
||||||
mop,
|
mop,
|
||||||
|
unit_index,
|
||||||
mop_in_unit_state: _,
|
mop_in_unit_state: _,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished,
|
all_prior_mops_are_finished: _,
|
||||||
|
output,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} = self;
|
} = self;
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
Self {
|
Self {
|
||||||
mop: zeroed(mop),
|
mop: zeroed(mop),
|
||||||
|
unit_index: zeroed(unit_index),
|
||||||
mop_in_unit_state: SimOnlyValue::default(),
|
mop_in_unit_state: SimOnlyValue::default(),
|
||||||
is_speculative: false,
|
is_speculative: false,
|
||||||
finished: #[hdl(sim)]
|
all_prior_mops_are_finished: false,
|
||||||
finished.HdlNone(),
|
output: #[hdl(sim)]
|
||||||
|
output.HdlNone(),
|
||||||
caused_cancel: #[hdl(sim)]
|
caused_cancel: #[hdl(sim)]
|
||||||
caused_cancel.HdlNone(),
|
caused_cancel.HdlNone(),
|
||||||
}
|
}
|
||||||
|
|
@ -600,53 +628,58 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct RobEntry<C: PhantomConstCpuConfig> {
|
struct RobEntry<C: PhantomConstCpuConfig> {
|
||||||
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||||
|
unit_index: usize,
|
||||||
mop_in_unit_state: MOpInUnitState,
|
mop_in_unit_state: MOpInUnitState,
|
||||||
is_speculative: bool,
|
is_speculative: bool,
|
||||||
finished: Option<SimValue<NextPcPredictorOp<C>>>,
|
all_prior_mops_are_finished: bool,
|
||||||
|
output: Option<SimValue<NextPcPredictorOp<C>>>,
|
||||||
caused_cancel: Option<SimValue<UnitCausedCancel<C>>>,
|
caused_cancel: Option<SimValue<UnitCausedCancel<C>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: PhantomConstCpuConfig> RobEntry<C> {
|
impl<C: PhantomConstCpuConfig> RobEntry<C> {
|
||||||
fn new(mop: SimValue<MOpInstance<RenamedMOp<C>>>) -> Self {
|
fn new(mop: SimValue<MOpInstance<RenamedMOp<C>>>, unit_index: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mop,
|
mop,
|
||||||
|
unit_index,
|
||||||
mop_in_unit_state: MOpInUnitState::NotYetEnqueued,
|
mop_in_unit_state: MOpInUnitState::NotYetEnqueued,
|
||||||
is_speculative: true,
|
is_speculative: true,
|
||||||
finished: None,
|
all_prior_mops_are_finished: false,
|
||||||
|
output: None,
|
||||||
caused_cancel: None,
|
caused_cancel: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn dest_reg(&self) -> &SimValue<PRegNum<C>> {
|
fn dest_reg(&self) -> Option<&SimValue<PRegNum<C>>> {
|
||||||
MOpTrait::dest_reg_sim_ref(&self.mop.mop)
|
let dest_reg = MOpTrait::dest_reg_sim_ref(&self.mop.mop);
|
||||||
|
let unit_index = UnitNum::index_sim(&dest_reg.unit_num)?;
|
||||||
|
assert_eq!(unit_index, self.unit_index);
|
||||||
|
Some(dest_reg)
|
||||||
}
|
}
|
||||||
fn unit_num(&self) -> &SimValue<UnitNum<C>> {
|
fn unit_out_reg(&self) -> Option<&SimValue<UnitOutRegNum<C>>> {
|
||||||
&self.dest_reg().unit_num
|
Some(&self.dest_reg()?.unit_out_reg)
|
||||||
}
|
}
|
||||||
fn unit_index(&self) -> usize {
|
fn unit_out_reg_index(&self) -> Option<usize> {
|
||||||
UnitNum::index_sim(&self.unit_num()).expect("known to have unit_index")
|
Some(UnitOutRegNum::value_sim(self.unit_out_reg()?))
|
||||||
}
|
|
||||||
fn unit_out_reg(&self) -> &SimValue<UnitOutRegNum<C>> {
|
|
||||||
&self.dest_reg().unit_out_reg
|
|
||||||
}
|
|
||||||
fn unit_out_reg_index(&self) -> usize {
|
|
||||||
UnitOutRegNum::value_sim(&self.unit_out_reg())
|
|
||||||
}
|
}
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn debug_state(&self, config: C) -> SimValue<RobEntryDebugState<C>> {
|
fn debug_state(&self, config: C) -> SimValue<RobEntryDebugState<C>> {
|
||||||
let Self {
|
let Self {
|
||||||
mop,
|
mop,
|
||||||
|
unit_index,
|
||||||
mop_in_unit_state,
|
mop_in_unit_state,
|
||||||
is_speculative,
|
is_speculative,
|
||||||
finished,
|
all_prior_mops_are_finished,
|
||||||
|
output,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} = self;
|
} = self;
|
||||||
let ret_ty = RobEntryDebugState[config];
|
let ret_ty = RobEntryDebugState[config];
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
RobEntryDebugState::<C> {
|
RobEntryDebugState::<C> {
|
||||||
mop,
|
mop,
|
||||||
|
unit_index: unit_index.into_sim_value_with_type(ret_ty.unit_index),
|
||||||
mop_in_unit_state: SimOnlyValue::new(*mop_in_unit_state),
|
mop_in_unit_state: SimOnlyValue::new(*mop_in_unit_state),
|
||||||
is_speculative,
|
is_speculative,
|
||||||
finished: finished.into_sim_value_with_type(ret_ty.finished),
|
all_prior_mops_are_finished,
|
||||||
|
output: output.into_sim_value_with_type(ret_ty.output),
|
||||||
caused_cancel: caused_cancel.into_sim_value_with_type(ret_ty.caused_cancel),
|
caused_cancel: caused_cancel.into_sim_value_with_type(ret_ty.caused_cancel),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -918,21 +951,24 @@ impl<C: PhantomConstCpuConfig> ReorderBuffer<C> {
|
||||||
&mut self,
|
&mut self,
|
||||||
unrenamed: &SimValue<MOpInstance<MOp>>,
|
unrenamed: &SimValue<MOpInstance<MOp>>,
|
||||||
mut renamed: RobEntry<C>,
|
mut renamed: RobEntry<C>,
|
||||||
) {
|
) -> &SimValue<MOpId> {
|
||||||
let replacement_id = self
|
let replacement_id = self
|
||||||
.next_renamed_mop_id
|
.next_renamed_mop_id
|
||||||
.as_int()
|
.as_int()
|
||||||
.wrapping_add(1)
|
.wrapping_add(1)
|
||||||
.into_sim_value();
|
.into_sim_value();
|
||||||
renamed.mop.id = mem::replace(&mut self.next_renamed_mop_id, replacement_id);
|
renamed.mop.id = mem::replace(&mut self.next_renamed_mop_id, replacement_id);
|
||||||
self.incomplete_back_entry
|
println!("renamed_push_back_with_new_id: {:?}", renamed.mop);
|
||||||
|
let renamed_entries = &mut self
|
||||||
|
.incomplete_back_entry
|
||||||
.get_or_insert_with(|| RobEntries {
|
.get_or_insert_with(|| RobEntries {
|
||||||
unrenamed: unrenamed.clone(),
|
unrenamed: unrenamed.clone(),
|
||||||
rename_table_updates: Vec::new(),
|
rename_table_updates: Vec::new(),
|
||||||
renamed_entries: VecDeque::new(),
|
renamed_entries: VecDeque::new(),
|
||||||
})
|
})
|
||||||
.renamed_entries
|
.renamed_entries;
|
||||||
.push_back(renamed);
|
renamed_entries.push_back(renamed);
|
||||||
|
&renamed_entries.back().expect("just pushed").mop.id
|
||||||
}
|
}
|
||||||
fn finished_unrenamed_push_back(&mut self, unrenamed: &SimValue<MOpInstance<MOp>>) {
|
fn finished_unrenamed_push_back(&mut self, unrenamed: &SimValue<MOpInstance<MOp>>) {
|
||||||
let entry = self
|
let entry = self
|
||||||
|
|
@ -978,9 +1014,21 @@ const SimOnlyString: SimOnlyString = SimOnlyString::TYPE;
|
||||||
#[hdl(get(|c| c.rob_size.get().next_power_of_two()))]
|
#[hdl(get(|c| c.rob_size.get().next_power_of_two()))]
|
||||||
type PerInsnTimelineLen<C: PhantomConstGet<CpuConfig>> = DynSize;
|
type PerInsnTimelineLen<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
struct RenameDelayedForL2Store<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
chosen_dest: PRegNum<C>,
|
||||||
|
l2_store_id: MOpId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
struct RenameDelayedEntry<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
mop: MOpInstance<MOp>,
|
||||||
|
delayed_for_l2_store: HdlOption<RenameDelayedForL2Store<C>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[hdl(no_static)]
|
#[hdl(no_static)]
|
||||||
pub struct RenameExecuteRetireDebugState<C: PhantomConstGet<CpuConfig>> {
|
pub struct RenameExecuteRetireDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||||
rename_delayed: ArrayVec<MOpInstance<MOp>, TwiceCpuConfigFetchWidth<C>>,
|
rename_delayed: ArrayVec<RenameDelayedEntry<C>, TwiceCpuConfigFetchWidth<C>>,
|
||||||
rename_table: RenameTableDebugState<C>,
|
rename_table: RenameTableDebugState<C>,
|
||||||
retire_rename_table: RenameTableDebugState<C>,
|
retire_rename_table: RenameTableDebugState<C>,
|
||||||
rob: ReorderBufferDebugState<C>,
|
rob: ReorderBufferDebugState<C>,
|
||||||
|
|
@ -990,18 +1038,20 @@ pub struct RenameExecuteRetireDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||||
ArrayType<HdlOption<PRegValue>, CpuConfig2PowOutRegNumWidth<C>>,
|
ArrayType<HdlOption<PRegValue>, CpuConfig2PowOutRegNumWidth<C>>,
|
||||||
CpuConfigUnitCount<C>,
|
CpuConfigUnitCount<C>,
|
||||||
>,
|
>,
|
||||||
|
lfsr: LFSR31,
|
||||||
per_insn_timeline: ArrayType<SimOnlyString, PerInsnTimelineLen<C>>,
|
per_insn_timeline: ArrayType<SimOnlyString, PerInsnTimelineLen<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct RenameExecuteRetireState<C: PhantomConstCpuConfig> {
|
struct RenameExecuteRetireState<C: PhantomConstCpuConfig> {
|
||||||
rename_delayed: VecDeque<SimValue<MOpInstance<MOp>>>,
|
rename_delayed: VecDeque<SimValue<RenameDelayedEntry<C>>>,
|
||||||
rename_table: RenameTable<C>,
|
rename_table: RenameTable<C>,
|
||||||
retire_rename_table: RenameTable<C>,
|
retire_rename_table: RenameTable<C>,
|
||||||
rob: ReorderBuffer<C>,
|
rob: ReorderBuffer<C>,
|
||||||
next_pc_canceling: Option<NextPcCancelingState>,
|
next_pc_canceling: Option<NextPcCancelingState>,
|
||||||
unit_canceling: Box<[bool]>,
|
unit_canceling: Box<[bool]>,
|
||||||
l1_reg_file: Box<[Box<[Option<SimValue<PRegValue>>]>]>,
|
l1_reg_file: Box<[Box<[Option<SimValue<PRegValue>>]>]>,
|
||||||
|
lfsr: SimValue<LFSR31>,
|
||||||
l2_reg_file_unit_index: usize,
|
l2_reg_file_unit_index: usize,
|
||||||
config: C,
|
config: C,
|
||||||
}
|
}
|
||||||
|
|
@ -1020,6 +1070,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
CpuConfigUnitCount[config]
|
CpuConfigUnitCount[config]
|
||||||
]
|
]
|
||||||
.into_boxed_slice(),
|
.into_boxed_slice(),
|
||||||
|
lfsr: LFSR31::new(),
|
||||||
l2_reg_file_unit_index: config
|
l2_reg_file_unit_index: config
|
||||||
.get()
|
.get()
|
||||||
.units
|
.units
|
||||||
|
|
@ -1040,31 +1091,38 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
let empty_string = SimOnlyValue::new(String::new());
|
let empty_string = SimOnlyValue::new(String::new());
|
||||||
let mut retval =
|
let mut retval =
|
||||||
SimValue::from_array_elements(retval_ty, (0..len).map(|_| empty_string.clone()));
|
SimValue::from_array_elements(retval_ty, (0..len).map(|_| empty_string.clone()));
|
||||||
for rob in self.rob.renamed() {
|
for RobEntry {
|
||||||
let masked_id = rob.mop.id.as_int() as usize & mask;
|
mop,
|
||||||
|
unit_index: _,
|
||||||
|
mop_in_unit_state,
|
||||||
|
is_speculative,
|
||||||
|
all_prior_mops_are_finished,
|
||||||
|
output,
|
||||||
|
caused_cancel,
|
||||||
|
} in self.rob.renamed()
|
||||||
|
{
|
||||||
|
let masked_id = mop.id.as_int() as usize & mask;
|
||||||
**retval[masked_id] = fmt::from_fn(|f| {
|
**retval[masked_id] = fmt::from_fn(|f| {
|
||||||
f.write_str(rob.mop_in_unit_state.debug_str())?;
|
f.write_str(mop_in_unit_state.debug_str())?;
|
||||||
if rob.finished.is_some() {
|
if *is_speculative {
|
||||||
f.write_str("(finished)")?;
|
f.write_str("(s)")?;
|
||||||
}
|
}
|
||||||
if rob.caused_cancel.is_some() {
|
if *all_prior_mops_are_finished {
|
||||||
|
f.write_str("(apf)")?;
|
||||||
|
}
|
||||||
|
if output.is_some() {
|
||||||
|
f.write_str("(output)")?;
|
||||||
|
}
|
||||||
|
if caused_cancel.is_some() {
|
||||||
f.write_str("(caused cancel)")?;
|
f.write_str("(caused cancel)")?;
|
||||||
}
|
}
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
": {}{:#x}{}: {:?}",
|
": {}{:#x}{}: {:?}",
|
||||||
if *rob.mop.is_first_mop_in_insn {
|
if *mop.is_first_mop_in_insn { "" } else { ".." },
|
||||||
""
|
mop.pc.as_int(),
|
||||||
} else {
|
if *mop.is_last_mop_in_insn { "" } else { ".." },
|
||||||
".."
|
mop.mop,
|
||||||
},
|
|
||||||
rob.mop.pc.as_int(),
|
|
||||||
if *rob.mop.is_last_mop_in_insn {
|
|
||||||
""
|
|
||||||
} else {
|
|
||||||
".."
|
|
||||||
},
|
|
||||||
rob.mop.mop,
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
@ -1079,16 +1137,17 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
state_for_debug: Expr<RenameExecuteRetireDebugState<C>>,
|
state_for_debug: Expr<RenameExecuteRetireDebugState<C>>,
|
||||||
) {
|
) {
|
||||||
let Self {
|
let Self {
|
||||||
ref rename_delayed,
|
rename_delayed,
|
||||||
ref rename_table,
|
rename_table,
|
||||||
ref retire_rename_table,
|
retire_rename_table,
|
||||||
ref rob,
|
rob,
|
||||||
ref next_pc_canceling,
|
next_pc_canceling,
|
||||||
ref unit_canceling,
|
unit_canceling,
|
||||||
ref l1_reg_file,
|
l1_reg_file,
|
||||||
|
lfsr,
|
||||||
l2_reg_file_unit_index: _,
|
l2_reg_file_unit_index: _,
|
||||||
config: _,
|
config: _,
|
||||||
} = *self;
|
} = self;
|
||||||
sim.write(
|
sim.write(
|
||||||
state_for_debug,
|
state_for_debug,
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
|
|
@ -1096,7 +1155,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
rename_delayed: state_for_debug
|
rename_delayed: state_for_debug
|
||||||
.ty()
|
.ty()
|
||||||
.rename_delayed
|
.rename_delayed
|
||||||
.from_iter_sim(zeroed(StaticType::TYPE), rename_delayed)
|
.from_iter_sim(zeroed(RenameDelayedEntry[self.config]), rename_delayed)
|
||||||
.expect("known to fit"),
|
.expect("known to fit"),
|
||||||
rename_table: rename_table.to_debug_state(),
|
rename_table: rename_table.to_debug_state(),
|
||||||
retire_rename_table: retire_rename_table.to_debug_state(),
|
retire_rename_table: retire_rename_table.to_debug_state(),
|
||||||
|
|
@ -1109,6 +1168,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
SimValue::from_array_elements(state_for_debug.ty().l1_reg_file.element(), v)
|
SimValue::from_array_elements(state_for_debug.ty().l1_reg_file.element(), v)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
lfsr,
|
||||||
per_insn_timeline: self.per_insn_timeline(),
|
per_insn_timeline: self.per_insn_timeline(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -1133,7 +1193,10 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
.HdlSome
|
.HdlSome
|
||||||
.from_iter_sim(
|
.from_iter_sim(
|
||||||
zeroed(MOpInstance[MOp]),
|
zeroed(MOpInstance[MOp]),
|
||||||
self.rename_delayed.iter().chain(self.rob.unrenamed()),
|
self.rename_delayed
|
||||||
|
.iter()
|
||||||
|
.map(|v| &v.mop)
|
||||||
|
.chain(self.rob.unrenamed()),
|
||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
.expect("known to fit"),
|
.expect("known to fit"),
|
||||||
|
|
@ -1145,7 +1208,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
fn space_available_for_unit(&self, unit_index: usize) -> usize {
|
fn space_available_for_unit(&self, unit_index: usize) -> usize {
|
||||||
let mut retval = self.config.get().unit_max_in_flight(unit_index);
|
let mut retval = self.config.get().unit_max_in_flight(unit_index);
|
||||||
for renamed in self.rob.renamed() {
|
for renamed in self.rob.renamed() {
|
||||||
if renamed.unit_index() == unit_index {
|
if renamed.unit_index == unit_index {
|
||||||
let Some(v) = NonZero::new(retval.get() - 1) else {
|
let Some(v) = NonZero::new(retval.get() - 1) else {
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
@ -1159,8 +1222,10 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
// TODO: replace searching through instructions and rename tables with tracking when regs are free
|
// TODO: replace searching through instructions and rename tables with tracking when regs are free
|
||||||
let mut allocated_regs = vec![false; 1 << self.config.get().out_reg_num_width];
|
let mut allocated_regs = vec![false; 1 << self.config.get().out_reg_num_width];
|
||||||
for renamed in self.rob.renamed() {
|
for renamed in self.rob.renamed() {
|
||||||
if renamed.unit_index() == unit_index {
|
if renamed.unit_index == unit_index
|
||||||
allocated_regs[renamed.unit_out_reg_index()] = true;
|
&& let Some(unit_out_reg_index) = renamed.unit_out_reg_index()
|
||||||
|
{
|
||||||
|
allocated_regs[unit_out_reg_index] = true;
|
||||||
}
|
}
|
||||||
MOpTrait::for_each_src_reg_sim_ref(&renamed.mop.mop, &mut |src_reg, _index| {
|
MOpTrait::for_each_src_reg_sim_ref(&renamed.mop.mop, &mut |src_reg, _index| {
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
|
|
@ -1226,9 +1291,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
&mut self,
|
&mut self,
|
||||||
unrenamed: &SimValue<MOpInstance<MOp>>,
|
unrenamed: &SimValue<MOpInstance<MOp>>,
|
||||||
renamed: RobEntry<C>,
|
renamed: RobEntry<C>,
|
||||||
) {
|
) -> &SimValue<MOpId> {
|
||||||
self.l1_reg_file[renamed.unit_index()][renamed.unit_out_reg_index()] = None;
|
if let Some(unit_out_reg_index) = renamed.unit_out_reg_index() {
|
||||||
self.rob.renamed_push_back_with_new_id(unrenamed, renamed);
|
self.l1_reg_file[renamed.unit_index][unit_out_reg_index] = None;
|
||||||
|
}
|
||||||
|
self.rob.renamed_push_back_with_new_id(unrenamed, renamed)
|
||||||
}
|
}
|
||||||
fn update_rename_table(
|
fn update_rename_table(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -1242,13 +1309,33 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
fn try_rename(
|
fn try_rename(
|
||||||
&mut self,
|
&mut self,
|
||||||
insn: SimValue<MOpInstance<MOp>>,
|
entry: SimValue<RenameDelayedEntry<C>>,
|
||||||
) -> Result<(), SimValue<MOpInstance<MOp>>> {
|
) -> Result<(), SimValue<RenameDelayedEntry<C>>> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
} = entry;
|
||||||
|
println!("try_rename: insn: {insn:?}");
|
||||||
if self.rob.unrenamed_len() >= self.config.get().rob_size.get() {
|
if self.rob.unrenamed_len() >= self.config.get().rob_size.get() {
|
||||||
return Err(insn);
|
println!("try_rename: unrenamed_len >= rob_size");
|
||||||
|
return Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if self.rob.renamed_len() >= self.config.get().rob_size.get() {
|
if self.rob.renamed_len() >= self.config.get().rob_size.get() {
|
||||||
return Err(insn);
|
println!("try_rename: renamed_len >= rob_size");
|
||||||
|
return Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let unit_kind = UnitMOp::kind_sim(&insn.mop);
|
let unit_kind = UnitMOp::kind_sim(&insn.mop);
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
|
|
@ -1296,43 +1383,170 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut chosen_unit = None;
|
let chosen_unit = #[hdl(sim)]
|
||||||
for (unit_index, unit_config) in self.config.get().units.iter().enumerate() {
|
if let HdlSome(l2_store) = &delayed_for_l2_store {
|
||||||
if unit_config.kind != unit_kind {
|
let RobEntry {
|
||||||
continue;
|
mop: _,
|
||||||
}
|
unit_index: _,
|
||||||
let cur_unit = ChosenUnit {
|
mop_in_unit_state: MOpInUnitState::FinishedAndOrCausedCancel,
|
||||||
unit_index,
|
is_speculative: _,
|
||||||
out_reg_num: self.find_free_unit_out_reg(unit_index),
|
all_prior_mops_are_finished: true,
|
||||||
space_available: self.space_available_for_unit(unit_index),
|
output: _,
|
||||||
|
caused_cancel: None,
|
||||||
|
} = self.rob.renamed_by_id(&l2_store.l2_store_id)
|
||||||
|
else {
|
||||||
|
println!("try_rename: delaying for l2 store that isn't yet finished");
|
||||||
|
return Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
let chosen_unit = chosen_unit.get_or_insert(cur_unit);
|
let unit_index =
|
||||||
if cur_unit.is_better_than(*chosen_unit) {
|
UnitNum::index_sim(&l2_store.chosen_dest.unit_num).expect("known to be some");
|
||||||
*chosen_unit = cur_unit;
|
assert_eq!(self.config.get().units[unit_index].kind, unit_kind);
|
||||||
|
ChosenUnit {
|
||||||
|
unit_index,
|
||||||
|
out_reg_num: Some(UnitOutRegNum::value_sim(&l2_store.chosen_dest.unit_out_reg)),
|
||||||
|
space_available: self.space_available_for_unit(unit_index),
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
let Some(ChosenUnit {
|
let mut chosen_unit = None;
|
||||||
|
for (unit_index, unit_config) in self.config.get().units.iter().enumerate() {
|
||||||
|
if unit_config.kind != unit_kind {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let cur_unit = ChosenUnit {
|
||||||
|
unit_index,
|
||||||
|
out_reg_num: self.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(chosen_unit) = chosen_unit else {
|
||||||
|
panic!(
|
||||||
|
"there are no units of kind: {unit_kind:?}:\n{:?}",
|
||||||
|
self.config,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
chosen_unit
|
||||||
|
};
|
||||||
|
let ChosenUnit {
|
||||||
unit_index,
|
unit_index,
|
||||||
out_reg_num,
|
out_reg_num,
|
||||||
space_available,
|
space_available,
|
||||||
}) = chosen_unit
|
} = chosen_unit;
|
||||||
else {
|
|
||||||
panic!(
|
|
||||||
"there are no units of kind: {unit_kind:?}:\n{:?}",
|
|
||||||
self.config,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
if space_available == 0 {
|
if space_available == 0 {
|
||||||
return Err(insn);
|
println!("try_rename: space_available = 0");
|
||||||
|
return Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let Some(out_reg_num) = out_reg_num else {
|
let Some(out_reg_num) = out_reg_num else {
|
||||||
return if self.space_available_for_unit(self.l2_reg_file_unit_index) > 0
|
println!("try_rename: out_reg_num = None");
|
||||||
|
if self.space_available_for_unit(self.l2_reg_file_unit_index) > 0
|
||||||
|
&& self.rename_table.used_unit_out_reg_count(unit_index)
|
||||||
|
>= (1 << self.config.get().out_reg_num_width)
|
||||||
&& let Some(l2_reg_index) = self.find_free_l2_reg()
|
&& let Some(l2_reg_index) = self.find_free_l2_reg()
|
||||||
{
|
{
|
||||||
todo!("maybe start a L2 register file store");
|
println!("try_rename: start L2 store");
|
||||||
} else {
|
let reg_to_free = LFSR31::next_sim(&mut self.lfsr) as usize
|
||||||
Err(insn)
|
% (1 << self.config.get().out_reg_num_width);
|
||||||
};
|
let reg_to_free = #[hdl(sim)]
|
||||||
|
PRegNum::<_> {
|
||||||
|
unit_num: UnitNum[self.config].from_index_sim(unit_index),
|
||||||
|
unit_out_reg: UnitOutRegNum[self.config].new_sim(reg_to_free),
|
||||||
|
};
|
||||||
|
println!("try_rename: picked {reg_to_free:?}");
|
||||||
|
let mut any_collisions = false;
|
||||||
|
MOpTrait::for_each_src_reg_sim_ref(&insn.mop, &mut |src_reg, _| {
|
||||||
|
let renamed =
|
||||||
|
&self.rename_table.entries[MOpRegNum::reg_num_sim(&src_reg) as usize];
|
||||||
|
println!(
|
||||||
|
"try_rename: checking that mop src reg ({renamed:?}) doesn't conflict with picked reg"
|
||||||
|
);
|
||||||
|
#[hdl(sim)]
|
||||||
|
match renamed {
|
||||||
|
RenameTableEntry::<_>::L1(v) => {
|
||||||
|
if reg_to_free == *v {
|
||||||
|
any_collisions = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RenameTableEntry::<_>::L2(_) => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if any_collisions {
|
||||||
|
println!(
|
||||||
|
"try_rename: attempted L2 store collides with one or more mOp input register(s)"
|
||||||
|
);
|
||||||
|
return Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let dest = L2RegNum::new_sim(l2_reg_index);
|
||||||
|
self.update_rename_table(
|
||||||
|
&insn,
|
||||||
|
RenameTableUpdate::UpdateForWriteL2Reg {
|
||||||
|
dest: dest.clone(),
|
||||||
|
src: reg_to_free.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let l2_store_id = self.add_renamed_with_new_id(
|
||||||
|
&insn,
|
||||||
|
RobEntry::new(
|
||||||
|
#[hdl(sim)]
|
||||||
|
MOpInstance::<_> {
|
||||||
|
fetch_block_id: insn.fetch_block_id,
|
||||||
|
id: MOpId.zero(), // filled in by add_renamed_with_new_id
|
||||||
|
pc: insn.pc,
|
||||||
|
predicted_next_pc: insn.pc,
|
||||||
|
size_in_bytes: insn.size_in_bytes,
|
||||||
|
is_first_mop_in_insn: insn.is_first_mop_in_insn,
|
||||||
|
is_last_mop_in_insn: insn.is_last_mop_in_insn,
|
||||||
|
mop: WriteL2RegMOp::write_l2_reg::<RenamedMOp<C>>(
|
||||||
|
PRegNum[self.config].const_zero(),
|
||||||
|
repeat(®_to_free, ConstUsize::<1>),
|
||||||
|
dest,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
self.l2_reg_file_unit_index,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store: #[hdl(sim)]
|
||||||
|
(delayed_for_l2_store.ty()).HdlSome(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedForL2Store::<_> {
|
||||||
|
chosen_dest: reg_to_free,
|
||||||
|
l2_store_id,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
let out_reg_num_sim = UnitOutRegNum[self.config].new_sim(out_reg_num);
|
let out_reg_num_sim = UnitOutRegNum[self.config].new_sim(out_reg_num);
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
|
|
@ -1404,11 +1618,26 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
needed_load,
|
needed_load,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
self.l2_reg_file_unit_index,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Ok(())
|
println!("try_rename: l2 reg file has no space and/or has no free output regs");
|
||||||
|
Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Err(insn)
|
println!("try_rename: l2 reg file has no space and/or has no free output regs");
|
||||||
|
Err(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store,
|
||||||
|
},
|
||||||
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let mop = UnitMOp::with_transformed_move_op_sim(
|
let mop = UnitMOp::with_transformed_move_op_sim(
|
||||||
|
|
@ -1441,6 +1670,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
is_last_mop_in_insn,
|
is_last_mop_in_insn,
|
||||||
mop,
|
mop,
|
||||||
},
|
},
|
||||||
|
unit_index,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
self.rob.finished_unrenamed_push_back(&insn);
|
self.rob.finished_unrenamed_push_back(&insn);
|
||||||
|
|
@ -1455,7 +1685,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
return retval; // separate variable to work around rust-analyzer parse error
|
return retval; // separate variable to work around rust-analyzer parse error
|
||||||
}
|
}
|
||||||
for rob in self.rob.renamed() {
|
for rob in self.rob.renamed() {
|
||||||
if rob.unit_index() == unit_index
|
if rob.unit_index == unit_index
|
||||||
&& let Some(_) = rob.mop_in_unit_state.after_enqueue()
|
&& let Some(_) = rob.mop_in_unit_state.after_enqueue()
|
||||||
{
|
{
|
||||||
let retval = #[hdl(sim)]
|
let retval = #[hdl(sim)]
|
||||||
|
|
@ -1483,7 +1713,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
let zero_reg = PRegNum[self.config].const_zero().into_sim_value();
|
let zero_reg = PRegNum[self.config].const_zero().into_sim_value();
|
||||||
let zero_value = zeroed(PRegValue);
|
let zero_value = zeroed(PRegValue);
|
||||||
for rob in self.rob.renamed() {
|
for rob in self.rob.renamed() {
|
||||||
if rob.unit_index() == unit_index
|
if rob.unit_index == unit_index
|
||||||
&& let Some(_) = rob.mop_in_unit_state.with_inputs_ready()
|
&& let Some(_) = rob.mop_in_unit_state.with_inputs_ready()
|
||||||
{
|
{
|
||||||
let mut src_values: [_; COMMON_MOP_SRC_LEN] =
|
let mut src_values: [_; COMMON_MOP_SRC_LEN] =
|
||||||
|
|
@ -1533,7 +1763,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
return retval; // separate variable to work around rust-analyzer parse error
|
return retval; // separate variable to work around rust-analyzer parse error
|
||||||
}
|
}
|
||||||
for rob in self.rob.renamed() {
|
for rob in self.rob.renamed() {
|
||||||
if rob.unit_index() == unit_index
|
if rob.unit_index == unit_index
|
||||||
&& !rob.is_speculative
|
&& !rob.is_speculative
|
||||||
&& let Some(_) = rob.mop_in_unit_state.without_speculative()
|
&& let Some(_) = rob.mop_in_unit_state.without_speculative()
|
||||||
{
|
{
|
||||||
|
|
@ -1561,21 +1791,24 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
} = output_ready;
|
} = output_ready;
|
||||||
assert!(!self.is_canceling());
|
assert!(!self.is_canceling());
|
||||||
let rob = self.rob.renamed_by_id_mut(&id);
|
let rob = self.rob.renamed_by_id_mut(&id);
|
||||||
let unit_index = rob.unit_index();
|
|
||||||
let out_reg_index = rob.unit_out_reg_index();
|
let out_reg_index = rob.unit_out_reg_index();
|
||||||
let RobEntry {
|
let RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index,
|
||||||
mop_in_unit_state,
|
mop_in_unit_state,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished,
|
all_prior_mops_are_finished: _,
|
||||||
|
output,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} = rob;
|
} = rob;
|
||||||
assert!(finished.is_none());
|
assert!(output.is_none());
|
||||||
assert!(caused_cancel.is_none());
|
assert!(caused_cancel.is_none());
|
||||||
let l1_reg = &mut self.l1_reg_file[unit_index][out_reg_index];
|
if let Some(out_reg_index) = out_reg_index {
|
||||||
assert!(l1_reg.is_none());
|
let l1_reg = &mut self.l1_reg_file[*unit_index][out_reg_index];
|
||||||
*l1_reg = Some(dest_value);
|
assert!(l1_reg.is_none());
|
||||||
*finished = Some(predictor_op);
|
*l1_reg = Some(dest_value);
|
||||||
|
}
|
||||||
|
*output = Some(predictor_op);
|
||||||
*mop_in_unit_state = mop_in_unit_state
|
*mop_in_unit_state = mop_in_unit_state
|
||||||
.after_output_ready()
|
.after_output_ready()
|
||||||
.expect("should be valid state for output to become ready");
|
.expect("should be valid state for output to become ready");
|
||||||
|
|
@ -1594,9 +1827,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
assert!(!self.is_canceling());
|
assert!(!self.is_canceling());
|
||||||
let RobEntry {
|
let RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index: _,
|
||||||
mop_in_unit_state,
|
mop_in_unit_state,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished,
|
all_prior_mops_are_finished: _,
|
||||||
|
output,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} = self.rob.renamed_by_id_mut(&id);
|
} = self.rob.renamed_by_id_mut(&id);
|
||||||
assert!(caused_cancel.is_none());
|
assert!(caused_cancel.is_none());
|
||||||
|
|
@ -1616,7 +1851,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
panic!(
|
panic!(
|
||||||
"MOp {id:?} made an invalid attempt to finish/cause a cancel:\n\
|
"MOp {id:?} made an invalid attempt to finish/cause a cancel:\n\
|
||||||
mop_in_unit_state={mop_in_unit_state:?}\n\
|
mop_in_unit_state={mop_in_unit_state:?}\n\
|
||||||
finished={finished:?}\n\
|
output={output:?}\n\
|
||||||
caused_cancel={caused_cancel:?}"
|
caused_cancel={caused_cancel:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1635,13 +1870,22 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[hdl]
|
||||||
fn handle_from_post_decode(&mut self, insns: &[SimValue<MOpInstance<MOp>>]) {
|
fn handle_from_post_decode(&mut self, insns: &[SimValue<MOpInstance<MOp>>]) {
|
||||||
if insns.is_empty() {
|
if self.is_canceling() {
|
||||||
|
assert!(insns.is_empty());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert!(!self.is_canceling());
|
|
||||||
for insn in insns {
|
for insn in insns {
|
||||||
self.rename_delayed.push_back(insn.clone());
|
self.rename_delayed.push_back(
|
||||||
|
#[hdl(sim)]
|
||||||
|
RenameDelayedEntry::<_> {
|
||||||
|
mop: insn,
|
||||||
|
delayed_for_l2_store: #[hdl(sim)]
|
||||||
|
(HdlOption[RenameDelayedForL2Store[self.config]])
|
||||||
|
.HdlNone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
for _ in 0..CpuConfigFetchWidth[self.config] {
|
for _ in 0..CpuConfigFetchWidth[self.config] {
|
||||||
let Some(insn) = self.rename_delayed.pop_front() else {
|
let Some(insn) = self.rename_delayed.pop_front() else {
|
||||||
|
|
@ -1666,6 +1910,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
next_pc_canceling,
|
next_pc_canceling,
|
||||||
unit_canceling: _,
|
unit_canceling: _,
|
||||||
l1_reg_file: _,
|
l1_reg_file: _,
|
||||||
|
lfsr: _,
|
||||||
l2_reg_file_unit_index: _,
|
l2_reg_file_unit_index: _,
|
||||||
config: _,
|
config: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
@ -1693,9 +1938,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
for renamed_entry in retire_group.clone().flat_map(|v| &v.renamed_entries) {
|
for renamed_entry in retire_group.clone().flat_map(|v| &v.renamed_entries) {
|
||||||
if let RobEntry {
|
if let RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index: _,
|
||||||
mop_in_unit_state: MOpInUnitState::FinishedAndOrCausedCancel,
|
mop_in_unit_state: MOpInUnitState::FinishedAndOrCausedCancel,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished,
|
all_prior_mops_are_finished: _,
|
||||||
|
output,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} = renamed_entry
|
} = renamed_entry
|
||||||
{
|
{
|
||||||
|
|
@ -1703,7 +1950,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
// only the part before the cancel needs to be ready
|
// only the part before the cancel needs to be ready
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert!(finished.is_some());
|
assert!(output.is_some());
|
||||||
} else {
|
} else {
|
||||||
// group isn't ready
|
// group isn't ready
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -1740,7 +1987,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
};
|
};
|
||||||
for renamed in renamed_entries {
|
for renamed in renamed_entries {
|
||||||
let Some(finished) = &renamed.finished else {
|
let Some(output) = &renamed.output else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
|
|
@ -1748,7 +1995,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
call_stack_op,
|
call_stack_op,
|
||||||
cond_br_taken,
|
cond_br_taken,
|
||||||
config: _,
|
config: _,
|
||||||
} = finished;
|
} = output;
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
if let CallStackOp::None = &unrenamed_op.call_stack_op {
|
if let CallStackOp::None = &unrenamed_op.call_stack_op {
|
||||||
unrenamed_op.call_stack_op = call_stack_op.clone();
|
unrenamed_op.call_stack_op = call_stack_op.clone();
|
||||||
|
|
@ -1824,9 +2071,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
.for_each(|v| self.retire_rename_table.update(v, "retire_rename_table"));
|
.for_each(|v| self.retire_rename_table.update(v, "retire_rename_table"));
|
||||||
for RobEntry {
|
for RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index: _,
|
||||||
mop_in_unit_state,
|
mop_in_unit_state,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished: _,
|
all_prior_mops_are_finished: _,
|
||||||
|
output: _,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} in renamed_entries
|
} in renamed_entries
|
||||||
{
|
{
|
||||||
|
|
@ -1855,6 +2104,12 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
if self.is_canceling() {
|
if self.is_canceling() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
for renamed in self.rob.renamed_mut() {
|
||||||
|
renamed.all_prior_mops_are_finished = true;
|
||||||
|
let MOpInUnitState::FinishedAndOrCausedCancel = renamed.mop_in_unit_state else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
for renamed in self.rob.renamed_mut() {
|
for renamed in self.rob.renamed_mut() {
|
||||||
let can_cause_cancel = match renamed.mop_in_unit_state {
|
let can_cause_cancel = match renamed.mop_in_unit_state {
|
||||||
MOpInUnitState::NotYetEnqueued => true,
|
MOpInUnitState::NotYetEnqueued => true,
|
||||||
|
|
@ -1877,9 +2132,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
||||||
let first_renamed = self.rob.renamed().next();
|
let first_renamed = self.rob.renamed().next();
|
||||||
if let Some(RobEntry {
|
if let Some(RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index: _,
|
||||||
mop_in_unit_state: MOpInUnitState::FinishedAndOrCausedCancel,
|
mop_in_unit_state: MOpInUnitState::FinishedAndOrCausedCancel,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished: _,
|
all_prior_mops_are_finished: _,
|
||||||
|
output: _,
|
||||||
caused_cancel: Some(caused_cancel),
|
caused_cancel: Some(caused_cancel),
|
||||||
}) = first_renamed
|
}) = first_renamed
|
||||||
&& !*caused_cancel.cancel_after_retire
|
&& !*caused_cancel.cancel_after_retire
|
||||||
|
|
@ -1987,12 +2244,14 @@ async fn rename_execute_retire_run(
|
||||||
assert!(!state.is_canceling());
|
assert!(!state.is_canceling());
|
||||||
let RobEntry {
|
let RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index: _,
|
||||||
mop_in_unit_state,
|
mop_in_unit_state,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished,
|
all_prior_mops_are_finished: _,
|
||||||
|
output,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} = state.rob.renamed_by_id_mut(&enqueue.mop.id);
|
} = state.rob.renamed_by_id_mut(&enqueue.mop.id);
|
||||||
assert!(finished.is_none());
|
assert!(output.is_none());
|
||||||
assert!(caused_cancel.is_none());
|
assert!(caused_cancel.is_none());
|
||||||
*mop_in_unit_state = mop_in_unit_state
|
*mop_in_unit_state = mop_in_unit_state
|
||||||
.after_enqueue()
|
.after_enqueue()
|
||||||
|
|
@ -2004,12 +2263,14 @@ async fn rename_execute_retire_run(
|
||||||
assert!(!state.is_canceling());
|
assert!(!state.is_canceling());
|
||||||
let RobEntry {
|
let RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index: _,
|
||||||
mop_in_unit_state,
|
mop_in_unit_state,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished,
|
all_prior_mops_are_finished: _,
|
||||||
|
output,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} = state.rob.renamed_by_id_mut(&inputs_ready.mop.id);
|
} = state.rob.renamed_by_id_mut(&inputs_ready.mop.id);
|
||||||
assert!(finished.is_none());
|
assert!(output.is_none());
|
||||||
assert!(caused_cancel.is_none());
|
assert!(caused_cancel.is_none());
|
||||||
*mop_in_unit_state = mop_in_unit_state
|
*mop_in_unit_state = mop_in_unit_state
|
||||||
.with_inputs_ready()
|
.with_inputs_ready()
|
||||||
|
|
@ -2022,13 +2283,13 @@ async fn rename_execute_retire_run(
|
||||||
assert!(!state.is_canceling());
|
assert!(!state.is_canceling());
|
||||||
let RobEntry {
|
let RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index: _,
|
||||||
mop_in_unit_state,
|
mop_in_unit_state,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished,
|
all_prior_mops_are_finished: _,
|
||||||
caused_cancel,
|
output: _,
|
||||||
|
caused_cancel: _,
|
||||||
} = state.rob.renamed_by_id_mut(&is_no_longer_speculative.id);
|
} = state.rob.renamed_by_id_mut(&is_no_longer_speculative.id);
|
||||||
assert!(finished.is_none());
|
|
||||||
assert!(caused_cancel.is_none());
|
|
||||||
*mop_in_unit_state = mop_in_unit_state
|
*mop_in_unit_state = mop_in_unit_state
|
||||||
.without_speculative()
|
.without_speculative()
|
||||||
.expect("UnitMOpIsNoLongerSpeculative is known to be valid");
|
.expect("UnitMOpIsNoLongerSpeculative is known to be valid");
|
||||||
|
|
@ -2041,12 +2302,13 @@ async fn rename_execute_retire_run(
|
||||||
assert!(!state.is_canceling());
|
assert!(!state.is_canceling());
|
||||||
let RobEntry {
|
let RobEntry {
|
||||||
mop: _,
|
mop: _,
|
||||||
|
unit_index: _,
|
||||||
mop_in_unit_state,
|
mop_in_unit_state,
|
||||||
is_speculative: _,
|
is_speculative: _,
|
||||||
finished,
|
all_prior_mops_are_finished: _,
|
||||||
|
output: _,
|
||||||
caused_cancel,
|
caused_cancel,
|
||||||
} = state.rob.renamed_by_id_mut(&id);
|
} = state.rob.renamed_by_id_mut(&id);
|
||||||
assert!(finished.is_none());
|
|
||||||
assert!(caused_cancel.is_none());
|
assert!(caused_cancel.is_none());
|
||||||
*mop_in_unit_state = mop_in_unit_state
|
*mop_in_unit_state = mop_in_unit_state
|
||||||
.with_cant_cause_cancel()
|
.with_cant_cause_cancel()
|
||||||
|
|
|
||||||
|
|
@ -280,3 +280,33 @@ impl<T: Type, N: Size> Rotate<usize> for SimValue<ArrayType<T, N>> {
|
||||||
retval
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
29465
crates/cpu/tests/expected/rename_execute_retire_fibonacci.vcd
generated
29465
crates/cpu/tests/expected/rename_execute_retire_fibonacci.vcd
generated
File diff suppressed because it is too large
Load diff
849480
crates/cpu/tests/expected/rename_execute_retire_slow_loop.vcd
generated
Normal file
849480
crates/cpu/tests/expected/rename_execute_retire_slow_loop.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -8,9 +8,10 @@ use cpu::{
|
||||||
},
|
},
|
||||||
instruction::{
|
instruction::{
|
||||||
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
|
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
|
||||||
CompareMOp, CompareMode, ConditionMode, L2RegisterFileMOp, LoadMOp, LoadStoreCommonMOp,
|
CompareMOp, CompareMode, ConditionMode, L2RegNum, L2RegisterFileMOp, LoadMOp,
|
||||||
LoadStoreConversion, LoadStoreMOp, LoadStoreWidth, MOp, MOpDestReg, MOpRegNum, MOpTrait,
|
LoadStoreCommonMOp, LoadStoreConversion, LoadStoreMOp, LoadStoreWidth, MOp, MOpDestReg,
|
||||||
MoveRegMOp, OutputIntegerMode, PRegNum, StoreMOp, UnitNum,
|
MOpRegNum, MOpTrait, MoveRegMOp, OutputIntegerMode, PRegNum, ReadL2RegMOp, StoreMOp,
|
||||||
|
UnitNum, WriteL2RegMOp,
|
||||||
},
|
},
|
||||||
next_pc::CallStackOp,
|
next_pc::CallStackOp,
|
||||||
register::{PRegFlags, PRegFlagsPowerISA, PRegValue},
|
register::{PRegFlags, PRegFlagsPowerISA, PRegValue},
|
||||||
|
|
@ -291,7 +292,34 @@ impl InsnsBuilder {
|
||||||
.cast_to_static::<SInt<_>>(),
|
.cast_to_static::<SInt<_>>(),
|
||||||
true,
|
true,
|
||||||
ConditionMode.SGt(),
|
ConditionMode.SGt(),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
)]
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
fn power_isa_beq(&mut self, target: InsnsBuilderLabel) {
|
||||||
|
let pc = self.pc;
|
||||||
|
self.add_insn(Insn::new_lazy(
|
||||||
|
4,
|
||||||
|
format!("beq {}", self.labels[target.0].name),
|
||||||
|
move |labels| {
|
||||||
|
[BranchMOp::branch_cond_ctr(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0),
|
||||||
|
MOpRegNum::const_zero(),
|
||||||
|
MOpRegNum::const_zero(),
|
||||||
|
],
|
||||||
|
labels[target.0]
|
||||||
|
.pc()
|
||||||
|
.wrapping_sub(pc)
|
||||||
|
.cast_to_static::<SInt<_>>(),
|
||||||
|
false,
|
||||||
|
ConditionMode.Eq(),
|
||||||
|
true,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
|
@ -843,6 +871,7 @@ fn mock_next_pc<#[hdl(skip)] MI: MakeInsns>(config: PhantomConst<CpuConfig>) {
|
||||||
state: Cell::new(0),
|
state: Cell::new(0),
|
||||||
};
|
};
|
||||||
let insns = MI::make_insns();
|
let insns = MI::make_insns();
|
||||||
|
dbg!(&insns);
|
||||||
sim.resettable(
|
sim.resettable(
|
||||||
cd,
|
cd,
|
||||||
async |mut sim| {
|
async |mut sim| {
|
||||||
|
|
@ -1272,7 +1301,12 @@ impl MockMemory {
|
||||||
src_values: &[SimValue<PRegValue>; COMMON_MOP_SRC_LEN],
|
src_values: &[SimValue<PRegValue>; COMMON_MOP_SRC_LEN],
|
||||||
is_speculative: bool,
|
is_speculative: bool,
|
||||||
) -> Result<SimValue<PRegValue>, AddressCantBeSpeculativelyAccessed> {
|
) -> Result<SimValue<PRegValue>, AddressCantBeSpeculativelyAccessed> {
|
||||||
#[hdl(sim)]
|
println!("MockMemory::run_mop: {:#x}: {:?}", mop.pc.as_int(), mop.mop);
|
||||||
|
println!(
|
||||||
|
"<- {}{src_values:?}",
|
||||||
|
if is_speculative { "(speculative) " } else { "" },
|
||||||
|
);
|
||||||
|
let retval = #[hdl(sim)]
|
||||||
match &mop.mop {
|
match &mop.mop {
|
||||||
LoadStoreMOp::<_, _>::Load(mop) => {
|
LoadStoreMOp::<_, _>::Load(mop) => {
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
|
|
@ -1323,13 +1357,11 @@ impl MockMemory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(
|
#[hdl(sim)]
|
||||||
#[hdl(sim)]
|
PRegValue {
|
||||||
PRegValue {
|
int_fp: loaded,
|
||||||
int_fp: loaded,
|
flags: PRegFlags::zeroed_sim(),
|
||||||
flags: PRegFlags::zeroed_sim(),
|
}
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
LoadStoreMOp::<_, _>::Store(mop) => {
|
LoadStoreMOp::<_, _>::Store(mop) => {
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
|
|
@ -1363,25 +1395,16 @@ impl MockMemory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(PRegValue::zeroed_sim())
|
PRegValue::zeroed_sim()
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
println!("-> {retval:?}");
|
||||||
|
Ok(retval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(no_static)]
|
|
||||||
struct MockL2RegFileDebugState {}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct MockL2RegFile {}
|
|
||||||
|
|
||||||
trait MockExecutionStateTrait: Default {
|
trait MockExecutionStateTrait: Default {
|
||||||
type DebugState: BundleType;
|
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 debug_state_ty() -> Self::DebugState;
|
||||||
fn zeroed_debug_state() -> SimValue<Self::DebugState>;
|
fn zeroed_debug_state() -> SimValue<Self::DebugState>;
|
||||||
fn debug_state(&self) -> SimValue<Self::DebugState>;
|
fn debug_state(&self) -> SimValue<Self::DebugState>;
|
||||||
|
|
@ -1732,46 +1755,17 @@ trait MockExecutionStateTrait: Default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
UnitMOp::<_, _, _>::TransformedMove(mop) => {
|
UnitMOp::<_, _, _>::TransformedMove(mop) => {
|
||||||
let l2_reg_file = self.get_l2_reg_file();
|
panic!(
|
||||||
#[hdl(sim)]
|
"mock_unit can't execute L2RegisterFile MOp, that needs mock_l2_reg_file_unit: {mop:#?}"
|
||||||
match mop {
|
);
|
||||||
L2RegisterFileMOp::<_, _>::ReadL2Reg(mop) => {
|
|
||||||
todo!("implement ReadL2Reg")
|
|
||||||
}
|
|
||||||
L2RegisterFileMOp::<_, _>::WriteL2Reg(mop) => {
|
|
||||||
todo!("implement WriteL2Reg")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
UnitMOp::<_, _, _>::Unknown => unreachable!(),
|
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 () {
|
impl MockExecutionStateTrait for () {
|
||||||
type DebugState = ();
|
type DebugState = ();
|
||||||
fn try_get_l2_reg_file(&mut self) -> Option<&mut MockL2RegFile> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn debug_state_ty() -> Self::DebugState {
|
fn debug_state_ty() -> Self::DebugState {
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
|
|
@ -2228,6 +2222,547 @@ 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]);
|
||||||
|
#[hdl]
|
||||||
|
let started_any: Bool = m.output();
|
||||||
|
m.register_clock_for_past(cd.clk);
|
||||||
|
m.extern_module_simulation_fn(
|
||||||
|
(cd, from_execute, debug_state, started_any, config),
|
||||||
|
async |args, mut sim| {
|
||||||
|
let (cd, from_execute, debug_state, started_any, 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.write(started_any, false).await;
|
||||||
|
},
|
||||||
|
|sim, ()| run_fn(cd, from_execute, debug_state, started_any, config, sim),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
#[hdl]
|
||||||
|
async fn run_fn(
|
||||||
|
cd: Expr<ClockDomain>,
|
||||||
|
from_execute: Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>>,
|
||||||
|
debug_state: Expr<MockL2RegFileUnitDebugState<PhantomConst<CpuConfig>>>,
|
||||||
|
started_any: Expr<Bool>,
|
||||||
|
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);
|
||||||
|
sim.write(started_any, true).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[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)]
|
#[hdl(no_static)]
|
||||||
struct MockLoadStoreOpDebugState<C: PhantomConstGet<CpuConfig>> {
|
struct MockLoadStoreOpDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||||
mop: MOpInstance<LoadStoreMOp<PRegNum<C>, PRegNum<C>>>,
|
mop: MOpInstance<LoadStoreMOp<PRegNum<C>, PRegNum<C>>>,
|
||||||
|
|
@ -2885,6 +3420,8 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: Phanto
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let all_outputs_written: Bool = m.output();
|
let all_outputs_written: Bool = m.output();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
let started_any_l2_reg_file_ops: Bool = m.output();
|
||||||
|
#[hdl]
|
||||||
let next_pc = instance(mock_next_pc::<MI>(config));
|
let next_pc = instance(mock_next_pc::<MI>(config));
|
||||||
connect(next_pc.cd, cd);
|
connect(next_pc.cd, cd);
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -2893,35 +3430,44 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: Phanto
|
||||||
connect(dut.from_post_decode, next_pc.post_decode_output);
|
connect(dut.from_post_decode, next_pc.post_decode_output);
|
||||||
connect(next_pc.from_retire, dut.to_next_pc);
|
connect(next_pc.from_retire, dut.to_next_pc);
|
||||||
connect(all_outputs_written, true);
|
connect(all_outputs_written, true);
|
||||||
|
connect(started_any_l2_reg_file_ops, false);
|
||||||
for (unit_index, to_unit) in ExecuteToUnitInterfaces::unit_fields(dut.to_units)
|
for (unit_index, to_unit) in ExecuteToUnitInterfaces::unit_fields(dut.to_units)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
if config.get().units[unit_index].kind == UnitKind::LoadStore {
|
match config.get().units[unit_index].kind {
|
||||||
let mock_unit = instance_with_loc(
|
UnitKind::LoadStore => {
|
||||||
&dut.ty().to_units.unit_field_name(unit_index),
|
let mock_unit = instance_with_loc(
|
||||||
mock_load_store_unit::<MI>(config, unit_index),
|
&dut.ty().to_units.unit_field_name(unit_index),
|
||||||
SourceLocation::caller(),
|
mock_load_store_unit::<MI>(config, unit_index),
|
||||||
);
|
SourceLocation::caller(),
|
||||||
connect(mock_unit.cd, cd);
|
);
|
||||||
connect(mock_unit.from_execute, to_unit);
|
connect(mock_unit.cd, cd);
|
||||||
#[hdl]
|
connect(mock_unit.from_execute, to_unit);
|
||||||
if !mock_unit.all_outputs_written {
|
#[hdl]
|
||||||
connect(all_outputs_written, false);
|
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);
|
||||||
|
connect(started_any_l2_reg_file_ops, mock_unit.started_any);
|
||||||
|
}
|
||||||
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2977,3 +3523,137 @@ fn test_rename_execute_retire_fibonacci() {
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SlowLoopInsns;
|
||||||
|
|
||||||
|
impl SlowLoopInsns {
|
||||||
|
const CONSTANTS_ADDR: u64 = 0x4000;
|
||||||
|
const CONSTANTS_COUNT: usize = 16;
|
||||||
|
const CONSTANTS_STEP: usize = 8;
|
||||||
|
const LOG2_RESULT_FACTOR: u32 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MakeInsns for SlowLoopInsns {
|
||||||
|
fn make_insns() -> Insns {
|
||||||
|
let mut b = InsnsBuilder::new();
|
||||||
|
|
||||||
|
let slow_loop = b.new_label("slow_loop");
|
||||||
|
b.power_isa_ld(3, 0, MockMemory::IO_ADDR as i16); // load input
|
||||||
|
b.power_isa_addi(1, 0, 0x4000); // setup stack pointer
|
||||||
|
b.power_isa_bl(slow_loop);
|
||||||
|
b.power_isa_std(3, 0, MockMemory::IO_ADDR as i16); // store output
|
||||||
|
let done = b.new_defined_label("done");
|
||||||
|
b.power_isa_b(done);
|
||||||
|
|
||||||
|
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_cmpldi(0, 3, 0);
|
||||||
|
let loop_done = b.new_label("loop_done");
|
||||||
|
b.power_isa_beq(loop_done); // if input == 0 goto loop_done
|
||||||
|
|
||||||
|
// long sequence of loads to provoke L2 register file store
|
||||||
|
let start_reg = 5;
|
||||||
|
assert!(
|
||||||
|
start_reg + Self::CONSTANTS_COUNT <= 32,
|
||||||
|
"too many constants to load them all into PowerISA GPRs",
|
||||||
|
);
|
||||||
|
for i in 0..Self::CONSTANTS_COUNT {
|
||||||
|
b.power_isa_ld(
|
||||||
|
start_reg + i,
|
||||||
|
0,
|
||||||
|
(Self::CONSTANTS_ADDR + (Self::CONSTANTS_STEP * i) as u64) as i16,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for i in 0..Self::CONSTANTS_COUNT {
|
||||||
|
b.power_isa_add(4, 4, start_reg + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
b.power_isa_addi(3, 3, -1);
|
||||||
|
b.power_isa_b(loop_header);
|
||||||
|
|
||||||
|
b.define_label(loop_done);
|
||||||
|
b.power_isa_mr(3, 4);
|
||||||
|
for _ in 0..Self::LOG2_RESULT_FACTOR {
|
||||||
|
b.power_isa_add(3, 3, 3);
|
||||||
|
}
|
||||||
|
b.power_isa_blr(); // return
|
||||||
|
|
||||||
|
b.build()
|
||||||
|
}
|
||||||
|
fn make_load_store_execution_state() -> MockMemory {
|
||||||
|
let expected = 0x0123_4567_89AB_CDEF_u64;
|
||||||
|
let constants: [[u8; Self::CONSTANTS_STEP]; Self::CONSTANTS_COUNT] =
|
||||||
|
std::array::from_fn(|i| {
|
||||||
|
let start_bit_index = i * 64 / Self::CONSTANTS_COUNT;
|
||||||
|
let end_bit_index = (i + 1) * 64 / Self::CONSTANTS_COUNT;
|
||||||
|
let start_bit = 1u64.unbounded_shl(start_bit_index as u32);
|
||||||
|
let end_bit = 1u64.unbounded_shl(end_bit_index as u32);
|
||||||
|
let mask = end_bit.wrapping_sub(start_bit);
|
||||||
|
(expected & mask).to_le_bytes()
|
||||||
|
});
|
||||||
|
let loop_count = 4;
|
||||||
|
MockMemory::new(
|
||||||
|
loop_count,
|
||||||
|
expected * loop_count * 2u64.pow(Self::LOG2_RESULT_FACTOR),
|
||||||
|
[(Self::CONSTANTS_ADDR, constants.as_flattened())],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
#[test]
|
||||||
|
fn test_rename_execute_retire_slow_loop() {
|
||||||
|
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::AluBranch),
|
||||||
|
UnitConfig::new(UnitKind::LoadStore),
|
||||||
|
UnitConfig::new(UnitKind::TransformedMove),
|
||||||
|
],
|
||||||
|
NonZeroUsize::new(20).unwrap(),
|
||||||
|
);
|
||||||
|
config.fetch_width = NonZeroUsize::new(4).unwrap();
|
||||||
|
let m = rename_execute_retire_test_harness::<SlowLoopInsns>(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..500 {
|
||||||
|
sim.advance_time(SimDuration::from_nanos(500));
|
||||||
|
println!("clock tick: {cycle}");
|
||||||
|
sim.write_clock(sim.io().cd.clk, true);
|
||||||
|
sim.advance_time(SimDuration::from_nanos(500));
|
||||||
|
sim.write_clock(sim.io().cd.clk, false);
|
||||||
|
sim.write_reset(sim.io().cd.rst, false);
|
||||||
|
}
|
||||||
|
assert!(sim.read_bool(sim.io().all_outputs_written));
|
||||||
|
// make sure we're actually testing L2 reg file ops
|
||||||
|
assert!(sim.read_bool(sim.io().started_any_l2_reg_file_ops));
|
||||||
|
// 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_slow_loop.vcd") {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue