Compare commits
1 commit
04e924460a
...
3fbdab0862
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fbdab0862 |
4 changed files with 635932 additions and 13047 deletions
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
},
|
||||
instruction::{
|
||||
COMMON_MOP_SRC_LEN, L2RegNum, L2RegisterFileMOp, MOp, MOpDestReg, MOpRegNum, MOpTrait,
|
||||
PRegNum, ReadL2RegMOp, UnitNum, UnitOutRegNum,
|
||||
PRegNum, ReadL2RegMOp, UnitNum, UnitOutRegNum, WriteL2RegMOp,
|
||||
},
|
||||
next_pc::{CallStackOp, SimValueDefault},
|
||||
register::PRegValue,
|
||||
|
|
@ -20,12 +20,16 @@ use crate::{
|
|||
util::{LFSR31, array_vec::ArrayVec},
|
||||
};
|
||||
use fayalite::{
|
||||
int::UIntInRangeInclusiveType,
|
||||
int::{UIntInRangeInclusiveType, UIntInRangeType},
|
||||
prelude::*,
|
||||
ty::{OpaqueSimValue, SimValueDebug, StaticType},
|
||||
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;
|
||||
|
||||
|
|
@ -381,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)]
|
||||
|
|
@ -570,8 +590,10 @@ type SimOnlyMOpInUnitState = SimOnly<MOpInUnitState>;
|
|||
#[hdl(no_static)]
|
||||
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
mop: MOpInstance<RenamedMOp<C>>,
|
||||
unit_index: UIntInRangeType<ConstUsize<0>, CpuConfigUnitCount<C>>,
|
||||
mop_in_unit_state: SimOnlyMOpInUnitState,
|
||||
is_speculative: Bool,
|
||||
all_prior_mops_are_finished: Bool,
|
||||
output: HdlOption<NextPcPredictorOp<C>>,
|
||||
caused_cancel: HdlOption<UnitCausedCancel<C>>,
|
||||
}
|
||||
|
|
@ -581,16 +603,20 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
|
|||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
mop,
|
||||
unit_index,
|
||||
mop_in_unit_state: _,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
mop: zeroed(mop),
|
||||
unit_index: zeroed(unit_index),
|
||||
mop_in_unit_state: SimOnlyValue::default(),
|
||||
is_speculative: false,
|
||||
all_prior_mops_are_finished: false,
|
||||
output: #[hdl(sim)]
|
||||
output.HdlNone(),
|
||||
caused_cancel: #[hdl(sim)]
|
||||
|
|
@ -602,43 +628,46 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
|
|||
#[derive(Debug)]
|
||||
struct RobEntry<C: PhantomConstCpuConfig> {
|
||||
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||
unit_index: usize,
|
||||
mop_in_unit_state: MOpInUnitState,
|
||||
is_speculative: bool,
|
||||
all_prior_mops_are_finished: bool,
|
||||
output: Option<SimValue<NextPcPredictorOp<C>>>,
|
||||
caused_cancel: Option<SimValue<UnitCausedCancel<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 {
|
||||
mop,
|
||||
unit_index,
|
||||
mop_in_unit_state: MOpInUnitState::NotYetEnqueued,
|
||||
is_speculative: true,
|
||||
all_prior_mops_are_finished: false,
|
||||
output: None,
|
||||
caused_cancel: None,
|
||||
}
|
||||
}
|
||||
fn dest_reg(&self) -> &SimValue<PRegNum<C>> {
|
||||
MOpTrait::dest_reg_sim_ref(&self.mop.mop)
|
||||
fn dest_reg(&self) -> Option<&SimValue<PRegNum<C>>> {
|
||||
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>> {
|
||||
&self.dest_reg().unit_num
|
||||
fn unit_out_reg(&self) -> Option<&SimValue<UnitOutRegNum<C>>> {
|
||||
Some(&self.dest_reg()?.unit_out_reg)
|
||||
}
|
||||
fn unit_index(&self) -> usize {
|
||||
UnitNum::index_sim(&self.unit_num()).expect("known to have unit_index")
|
||||
}
|
||||
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())
|
||||
fn unit_out_reg_index(&self) -> Option<usize> {
|
||||
Some(UnitOutRegNum::value_sim(self.unit_out_reg()?))
|
||||
}
|
||||
#[hdl]
|
||||
fn debug_state(&self, config: C) -> SimValue<RobEntryDebugState<C>> {
|
||||
let Self {
|
||||
mop,
|
||||
unit_index,
|
||||
mop_in_unit_state,
|
||||
is_speculative,
|
||||
all_prior_mops_are_finished,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = self;
|
||||
|
|
@ -646,8 +675,10 @@ impl<C: PhantomConstCpuConfig> RobEntry<C> {
|
|||
#[hdl(sim)]
|
||||
RobEntryDebugState::<C> {
|
||||
mop,
|
||||
unit_index: unit_index.into_sim_value_with_type(ret_ty.unit_index),
|
||||
mop_in_unit_state: SimOnlyValue::new(*mop_in_unit_state),
|
||||
is_speculative,
|
||||
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),
|
||||
}
|
||||
|
|
@ -920,21 +951,24 @@ impl<C: PhantomConstCpuConfig> ReorderBuffer<C> {
|
|||
&mut self,
|
||||
unrenamed: &SimValue<MOpInstance<MOp>>,
|
||||
mut renamed: RobEntry<C>,
|
||||
) {
|
||||
) -> &SimValue<MOpId> {
|
||||
let replacement_id = self
|
||||
.next_renamed_mop_id
|
||||
.as_int()
|
||||
.wrapping_add(1)
|
||||
.into_sim_value();
|
||||
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 {
|
||||
unrenamed: unrenamed.clone(),
|
||||
rename_table_updates: Vec::new(),
|
||||
renamed_entries: VecDeque::new(),
|
||||
})
|
||||
.renamed_entries
|
||||
.push_back(renamed);
|
||||
.renamed_entries;
|
||||
renamed_entries.push_back(renamed);
|
||||
&renamed_entries.back().expect("just pushed").mop.id
|
||||
}
|
||||
fn finished_unrenamed_push_back(&mut self, unrenamed: &SimValue<MOpInstance<MOp>>) {
|
||||
let entry = self
|
||||
|
|
@ -980,15 +1014,21 @@ 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 {
|
||||
#[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>,
|
||||
started_l2_store: Bool,
|
||||
delayed_for_l2_store: HdlOption<RenameDelayedForL2Store<C>>,
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
pub struct RenameExecuteRetireDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
rename_delayed: ArrayVec<RenameDelayedEntry, TwiceCpuConfigFetchWidth<C>>,
|
||||
rename_delayed: ArrayVec<RenameDelayedEntry<C>, TwiceCpuConfigFetchWidth<C>>,
|
||||
rename_table: RenameTableDebugState<C>,
|
||||
retire_rename_table: RenameTableDebugState<C>,
|
||||
rob: ReorderBufferDebugState<C>,
|
||||
|
|
@ -1004,7 +1044,7 @@ pub struct RenameExecuteRetireDebugState<C: PhantomConstGet<CpuConfig>> {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct RenameExecuteRetireState<C: PhantomConstCpuConfig> {
|
||||
rename_delayed: VecDeque<SimValue<RenameDelayedEntry>>,
|
||||
rename_delayed: VecDeque<SimValue<RenameDelayedEntry<C>>>,
|
||||
rename_table: RenameTable<C>,
|
||||
retire_rename_table: RenameTable<C>,
|
||||
rob: ReorderBuffer<C>,
|
||||
|
|
@ -1051,31 +1091,38 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
let empty_string = SimOnlyValue::new(String::new());
|
||||
let mut retval =
|
||||
SimValue::from_array_elements(retval_ty, (0..len).map(|_| empty_string.clone()));
|
||||
for rob in self.rob.renamed() {
|
||||
let masked_id = rob.mop.id.as_int() as usize & mask;
|
||||
for RobEntry {
|
||||
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| {
|
||||
f.write_str(rob.mop_in_unit_state.debug_str())?;
|
||||
if rob.output.is_some() {
|
||||
f.write_str(mop_in_unit_state.debug_str())?;
|
||||
if *is_speculative {
|
||||
f.write_str("(s)")?;
|
||||
}
|
||||
if *all_prior_mops_are_finished {
|
||||
f.write_str("(apf)")?;
|
||||
}
|
||||
if output.is_some() {
|
||||
f.write_str("(output)")?;
|
||||
}
|
||||
if rob.caused_cancel.is_some() {
|
||||
if caused_cancel.is_some() {
|
||||
f.write_str("(caused cancel)")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
": {}{:#x}{}: {:?}",
|
||||
if *rob.mop.is_first_mop_in_insn {
|
||||
""
|
||||
} else {
|
||||
".."
|
||||
},
|
||||
rob.mop.pc.as_int(),
|
||||
if *rob.mop.is_last_mop_in_insn {
|
||||
""
|
||||
} else {
|
||||
".."
|
||||
},
|
||||
rob.mop.mop,
|
||||
if *mop.is_first_mop_in_insn { "" } else { ".." },
|
||||
mop.pc.as_int(),
|
||||
if *mop.is_last_mop_in_insn { "" } else { ".." },
|
||||
mop.mop,
|
||||
)
|
||||
})
|
||||
.to_string();
|
||||
|
|
@ -1108,7 +1155,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
rename_delayed: state_for_debug
|
||||
.ty()
|
||||
.rename_delayed
|
||||
.from_iter_sim(zeroed(StaticType::TYPE), rename_delayed)
|
||||
.from_iter_sim(zeroed(RenameDelayedEntry[self.config]), rename_delayed)
|
||||
.expect("known to fit"),
|
||||
rename_table: rename_table.to_debug_state(),
|
||||
retire_rename_table: retire_rename_table.to_debug_state(),
|
||||
|
|
@ -1161,7 +1208,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
fn space_available_for_unit(&self, unit_index: usize) -> usize {
|
||||
let mut retval = self.config.get().unit_max_in_flight(unit_index);
|
||||
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 {
|
||||
return 0;
|
||||
};
|
||||
|
|
@ -1175,8 +1222,10 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
// 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];
|
||||
for renamed in self.rob.renamed() {
|
||||
if renamed.unit_index() == unit_index {
|
||||
allocated_regs[renamed.unit_out_reg_index()] = true;
|
||||
if renamed.unit_index == unit_index
|
||||
&& 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| {
|
||||
#[hdl(sim)]
|
||||
|
|
@ -1242,9 +1291,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
&mut self,
|
||||
unrenamed: &SimValue<MOpInstance<MOp>>,
|
||||
renamed: RobEntry<C>,
|
||||
) {
|
||||
self.l1_reg_file[renamed.unit_index()][renamed.unit_out_reg_index()] = None;
|
||||
self.rob.renamed_push_back_with_new_id(unrenamed, renamed);
|
||||
) -> &SimValue<MOpId> {
|
||||
if let Some(unit_out_reg_index) = renamed.unit_out_reg_index() {
|
||||
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(
|
||||
&mut self,
|
||||
|
|
@ -1258,21 +1309,21 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
#[hdl]
|
||||
fn try_rename(
|
||||
&mut self,
|
||||
entry: SimValue<RenameDelayedEntry>,
|
||||
) -> Result<(), SimValue<RenameDelayedEntry>> {
|
||||
entry: SimValue<RenameDelayedEntry<C>>,
|
||||
) -> Result<(), SimValue<RenameDelayedEntry<C>>> {
|
||||
#[hdl(sim)]
|
||||
let RenameDelayedEntry {
|
||||
let RenameDelayedEntry::<_> {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
delayed_for_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(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
RenameDelayedEntry::<_> {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
delayed_for_l2_store,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -1280,9 +1331,9 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
println!("try_rename: renamed_len >= rob_size");
|
||||
return Err(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
RenameDelayedEntry::<_> {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
delayed_for_l2_store,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -1332,58 +1383,170 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
}
|
||||
}
|
||||
}
|
||||
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 = #[hdl(sim)]
|
||||
if let HdlSome(l2_store) = &delayed_for_l2_store {
|
||||
let RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state: MOpInUnitState::FinishedAndOrCausedCancel,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: true,
|
||||
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);
|
||||
if cur_unit.is_better_than(*chosen_unit) {
|
||||
*chosen_unit = cur_unit;
|
||||
let unit_index =
|
||||
UnitNum::index_sim(&l2_store.chosen_dest.unit_num).expect("known to be some");
|
||||
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),
|
||||
}
|
||||
}
|
||||
let Some(ChosenUnit {
|
||||
} else {
|
||||
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,
|
||||
out_reg_num,
|
||||
space_available,
|
||||
}) = chosen_unit
|
||||
else {
|
||||
panic!(
|
||||
"there are no units of kind: {unit_kind:?}:\n{:?}",
|
||||
self.config,
|
||||
);
|
||||
};
|
||||
} = chosen_unit;
|
||||
if space_available == 0 {
|
||||
println!("try_rename: space_available = 0");
|
||||
return Err(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
RenameDelayedEntry::<_> {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
delayed_for_l2_store,
|
||||
},
|
||||
);
|
||||
}
|
||||
let Some(out_reg_num) = out_reg_num else {
|
||||
println!("try_rename: out_reg_num = None");
|
||||
return if !*started_l2_store
|
||||
&& self.space_available_for_unit(self.l2_reg_file_unit_index) > 0
|
||||
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()
|
||||
{
|
||||
todo!("maybe start a L2 register file store");
|
||||
} else {
|
||||
Err(
|
||||
println!("try_rename: start L2 store");
|
||||
let reg_to_free = LFSR31::next_sim(&mut self.lfsr) as usize
|
||||
% (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)]
|
||||
RenameDelayedEntry {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
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);
|
||||
#[hdl(sim)]
|
||||
|
|
@ -1455,16 +1618,24 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
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 {
|
||||
println!("try_rename: l2 reg file has no space and/or has no free output regs");
|
||||
Err(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
RenameDelayedEntry::<_> {
|
||||
mop: insn,
|
||||
started_l2_store,
|
||||
delayed_for_l2_store,
|
||||
},
|
||||
)
|
||||
};
|
||||
|
|
@ -1499,6 +1670,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
is_last_mop_in_insn,
|
||||
mop,
|
||||
},
|
||||
unit_index,
|
||||
),
|
||||
);
|
||||
self.rob.finished_unrenamed_push_back(&insn);
|
||||
|
|
@ -1513,7 +1685,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
return retval; // separate variable to work around rust-analyzer parse error
|
||||
}
|
||||
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 retval = #[hdl(sim)]
|
||||
|
|
@ -1541,7 +1713,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
let zero_reg = PRegNum[self.config].const_zero().into_sim_value();
|
||||
let zero_value = zeroed(PRegValue);
|
||||
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 mut src_values: [_; COMMON_MOP_SRC_LEN] =
|
||||
|
|
@ -1591,7 +1763,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
return retval; // separate variable to work around rust-analyzer parse error
|
||||
}
|
||||
for rob in self.rob.renamed() {
|
||||
if rob.unit_index() == unit_index
|
||||
if rob.unit_index == unit_index
|
||||
&& !rob.is_speculative
|
||||
&& let Some(_) = rob.mop_in_unit_state.without_speculative()
|
||||
{
|
||||
|
|
@ -1619,20 +1791,23 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
} = output_ready;
|
||||
assert!(!self.is_canceling());
|
||||
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 RobEntry {
|
||||
mop: _,
|
||||
unit_index,
|
||||
mop_in_unit_state,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = rob;
|
||||
assert!(output.is_none());
|
||||
assert!(caused_cancel.is_none());
|
||||
let l1_reg = &mut self.l1_reg_file[unit_index][out_reg_index];
|
||||
assert!(l1_reg.is_none());
|
||||
*l1_reg = Some(dest_value);
|
||||
if let Some(out_reg_index) = out_reg_index {
|
||||
let l1_reg = &mut self.l1_reg_file[*unit_index][out_reg_index];
|
||||
assert!(l1_reg.is_none());
|
||||
*l1_reg = Some(dest_value);
|
||||
}
|
||||
*output = Some(predictor_op);
|
||||
*mop_in_unit_state = mop_in_unit_state
|
||||
.after_output_ready()
|
||||
|
|
@ -1652,8 +1827,10 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
assert!(!self.is_canceling());
|
||||
let RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = self.rob.renamed_by_id_mut(&id);
|
||||
|
|
@ -1702,9 +1879,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
for insn in insns {
|
||||
self.rename_delayed.push_back(
|
||||
#[hdl(sim)]
|
||||
RenameDelayedEntry {
|
||||
RenameDelayedEntry::<_> {
|
||||
mop: insn,
|
||||
started_l2_store: false,
|
||||
delayed_for_l2_store: #[hdl(sim)]
|
||||
(HdlOption[RenameDelayedForL2Store[self.config]])
|
||||
.HdlNone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -1759,8 +1938,10 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
for renamed_entry in retire_group.clone().flat_map(|v| &v.renamed_entries) {
|
||||
if let RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state: MOpInUnitState::FinishedAndOrCausedCancel,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = renamed_entry
|
||||
|
|
@ -1890,8 +2071,10 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
.for_each(|v| self.retire_rename_table.update(v, "retire_rename_table"));
|
||||
for RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output: _,
|
||||
caused_cancel,
|
||||
} in renamed_entries
|
||||
|
|
@ -1921,6 +2104,12 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
if self.is_canceling() {
|
||||
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() {
|
||||
let can_cause_cancel = match renamed.mop_in_unit_state {
|
||||
MOpInUnitState::NotYetEnqueued => true,
|
||||
|
|
@ -1943,8 +2132,10 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
let first_renamed = self.rob.renamed().next();
|
||||
if let Some(RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state: MOpInUnitState::FinishedAndOrCausedCancel,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output: _,
|
||||
caused_cancel: Some(caused_cancel),
|
||||
}) = first_renamed
|
||||
|
|
@ -2053,8 +2244,10 @@ async fn rename_execute_retire_run(
|
|||
assert!(!state.is_canceling());
|
||||
let RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = state.rob.renamed_by_id_mut(&enqueue.mop.id);
|
||||
|
|
@ -2070,8 +2263,10 @@ async fn rename_execute_retire_run(
|
|||
assert!(!state.is_canceling());
|
||||
let RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = state.rob.renamed_by_id_mut(&inputs_ready.mop.id);
|
||||
|
|
@ -2088,8 +2283,10 @@ async fn rename_execute_retire_run(
|
|||
assert!(!state.is_canceling());
|
||||
let RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output: _,
|
||||
caused_cancel: _,
|
||||
} = state.rob.renamed_by_id_mut(&is_no_longer_speculative.id);
|
||||
|
|
@ -2105,8 +2302,10 @@ async fn rename_execute_retire_run(
|
|||
assert!(!state.is_canceling());
|
||||
let RobEntry {
|
||||
mop: _,
|
||||
unit_index: _,
|
||||
mop_in_unit_state,
|
||||
is_speculative: _,
|
||||
all_prior_mops_are_finished: _,
|
||||
output: _,
|
||||
caused_cancel,
|
||||
} = state.rob.renamed_by_id_mut(&id);
|
||||
|
|
|
|||
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
619092
crates/cpu/tests/expected/rename_execute_retire_slow_loop.vcd
generated
619092
crates/cpu/tests/expected/rename_execute_retire_slow_loop.vcd
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2609,11 +2609,13 @@ fn mock_l2_reg_file_unit(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
|||
#[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, config),
|
||||
(cd, from_execute, debug_state, started_any, config),
|
||||
async |args, mut sim| {
|
||||
let (cd, from_execute, debug_state, config) = args;
|
||||
let (cd, from_execute, debug_state, started_any, config) = args;
|
||||
sim.resettable(
|
||||
cd,
|
||||
async |mut sim| {
|
||||
|
|
@ -2659,8 +2661,9 @@ fn mock_l2_reg_file_unit(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
|||
},
|
||||
)
|
||||
.await;
|
||||
sim.write(started_any, false).await;
|
||||
},
|
||||
|sim, ()| run_fn(cd, from_execute, debug_state, config, sim),
|
||||
|sim, ()| run_fn(cd, from_execute, debug_state, started_any, config, sim),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
|
|
@ -2670,6 +2673,7 @@ fn mock_l2_reg_file_unit(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
|||
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,
|
||||
) {
|
||||
|
|
@ -2715,6 +2719,7 @@ fn mock_l2_reg_file_unit(config: PhantomConst<CpuConfig>, unit_index: usize) {
|
|||
#[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)]
|
||||
|
|
@ -3415,6 +3420,8 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: Phanto
|
|||
#[hdl]
|
||||
let all_outputs_written: Bool = m.output();
|
||||
#[hdl]
|
||||
let started_any_l2_reg_file_ops: Bool = m.output();
|
||||
#[hdl]
|
||||
let next_pc = instance(mock_next_pc::<MI>(config));
|
||||
connect(next_pc.cd, cd);
|
||||
#[hdl]
|
||||
|
|
@ -3423,6 +3430,7 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: Phanto
|
|||
connect(dut.from_post_decode, next_pc.post_decode_output);
|
||||
connect(next_pc.from_retire, dut.to_next_pc);
|
||||
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)
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
|
|
@ -3449,6 +3457,7 @@ fn rename_execute_retire_test_harness<#[hdl(skip)] MI: MakeInsns>(config: Phanto
|
|||
);
|
||||
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(
|
||||
|
|
@ -3630,7 +3639,7 @@ fn test_rename_execute_retire_slow_loop() {
|
|||
};
|
||||
sim.write_clock(sim.io().cd.clk, false);
|
||||
sim.write_reset(sim.io().cd.rst, true);
|
||||
for cycle in 0..200 {
|
||||
for cycle in 0..500 {
|
||||
sim.advance_time(SimDuration::from_nanos(500));
|
||||
println!("clock tick: {cycle}");
|
||||
sim.write_clock(sim.io().cd.clk, true);
|
||||
|
|
@ -3639,6 +3648,8 @@ fn test_rename_execute_retire_slow_loop() {
|
|||
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#######");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue