add a procedural implementation of rename_execute_retire #12
3 changed files with 638 additions and 604 deletions
|
|
@ -15,22 +15,24 @@ use crate::{
|
|||
},
|
||||
next_pc::{CallStackOp, SimValueDefault},
|
||||
register::PRegValue,
|
||||
rename_execute_retire::to_unit_interfaces::ExecuteToUnitInterfaces,
|
||||
rename_execute_retire::{
|
||||
rename_table::{RenameTable, RenameTableDebugState, RenameTableEntry, RenameTableUpdate},
|
||||
reorder_buffer::{ReorderBuffer, ReorderBufferDebugState, RobEntries, RobEntry},
|
||||
to_unit_interfaces::ExecuteToUnitInterfaces,
|
||||
},
|
||||
unit::{UnitKind, UnitMOp},
|
||||
util::{LFSR31, array_vec::ArrayVec},
|
||||
};
|
||||
use fayalite::{
|
||||
int::{UIntInRangeInclusiveType, UIntInRangeType},
|
||||
int::UIntInRangeInclusiveType,
|
||||
prelude::*,
|
||||
ty::{OpaqueSimValue, SimValueDebug, StaticType},
|
||||
util::ready_valid::ReadyValid,
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeSet, VecDeque},
|
||||
fmt, mem,
|
||||
num::NonZero,
|
||||
};
|
||||
use std::{collections::VecDeque, fmt, num::NonZero};
|
||||
|
||||
mod rename_table;
|
||||
mod reorder_buffer;
|
||||
pub mod to_unit_interfaces;
|
||||
|
||||
pub const MOP_ID_WIDTH: usize = 16;
|
||||
|
|
@ -255,192 +257,6 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RenameExecuteRetireDebugState
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct RenameTableEntry<C: PhantomConstGet<CpuConfig>> {
|
||||
l1: HdlOption<PRegNum<C>>,
|
||||
l2: HdlOption<L2RegNum>,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> RenameTableEntry<C> {
|
||||
#[hdl]
|
||||
fn const_zero(self) -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
l1: #[hdl(sim)]
|
||||
(self.l1).HdlSome(self.l1.HdlSome.const_zero()),
|
||||
l2: #[hdl(sim)]
|
||||
HdlNone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// make arrays dynamically-sized to avoid putting large types on the stack
|
||||
#[hdl(get(|c| 1 << MOpRegNum::WIDTH))]
|
||||
type MOpRegCount<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct RenameTableDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
entries: ArrayType<TraceAsString<RenameTableEntry<C>>, MOpRegCount<C>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RenameTable<C: PhantomConstCpuConfig> {
|
||||
entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> Clone for RenameTable<C> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
entries: self.entries.clone(),
|
||||
config: self.config.clone(),
|
||||
}
|
||||
}
|
||||
fn clone_from(&mut self, source: &Self) {
|
||||
let Self { entries, config } = self;
|
||||
entries.clone_from(&source.entries);
|
||||
*config = source.config;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum RenameTableUpdate<C: PhantomConstCpuConfig> {
|
||||
Write {
|
||||
unrenamed_reg_num: u32,
|
||||
new: SimValue<TraceAsString<RenameTableEntry<C>>>,
|
||||
},
|
||||
UpdateForReadL2Reg {
|
||||
dest: SimValue<PRegNum<C>>,
|
||||
src: SimValue<L2RegNum>,
|
||||
},
|
||||
UpdateForWriteL2Reg {
|
||||
dest: SimValue<L2RegNum>,
|
||||
src: SimValue<PRegNum<C>>,
|
||||
},
|
||||
DropAllL2RegFileOutputs,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> RenameTable<C> {
|
||||
fn new(config: C) -> Self {
|
||||
let entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]> =
|
||||
vec![
|
||||
RenameTableEntry[config].const_zero().into_trace_as_string();
|
||||
1 << MOpRegNum::WIDTH
|
||||
]
|
||||
.try_into()
|
||||
.expect("size is known to match");
|
||||
Self { entries, config }
|
||||
}
|
||||
#[hdl]
|
||||
fn to_debug_state(&self) -> SimValue<RenameTableDebugState<C>> {
|
||||
let Self { entries, config } = self;
|
||||
let ty = RenameTableDebugState[*config];
|
||||
#[hdl(sim)]
|
||||
RenameTableDebugState::<_> {
|
||||
entries: entries.to_sim_value_with_type(ty.entries),
|
||||
config,
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn update(&mut self, update: &RenameTableUpdate<C>, rename_table_name: &str) {
|
||||
match update {
|
||||
RenameTableUpdate::Write {
|
||||
unrenamed_reg_num,
|
||||
new,
|
||||
} => {
|
||||
if *unrenamed_reg_num == MOpRegNum::CONST_ZERO_REG_NUM {
|
||||
// writing to const zero reg does nothing
|
||||
return;
|
||||
}
|
||||
println!("{rename_table_name}: Write: {unrenamed_reg_num:#x} <- {new:?}");
|
||||
self.entries[*unrenamed_reg_num as usize] = new.clone();
|
||||
}
|
||||
RenameTableUpdate::UpdateForReadL2Reg { dest, src } => {
|
||||
let new = #[hdl(sim)]
|
||||
RenameTableEntry::<_> {
|
||||
l1: #[hdl(sim)]
|
||||
(HdlOption[dest.ty()]).HdlSome(dest),
|
||||
l2: #[hdl(sim)]
|
||||
HdlSome(src),
|
||||
};
|
||||
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(l2) = &entry.inner().l2 {
|
||||
if L2RegNum::value_sim(l2) == L2RegNum::value_sim(src) {
|
||||
println!(
|
||||
"{rename_table_name}: UpdateForReadL2Reg: {unrenamed_reg_num:#x} \
|
||||
updating from {entry:?} to {new:?}",
|
||||
);
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(_) = &entry.inner().l1 {
|
||||
unreachable!("l1 should be HdlNone: {entry:?}");
|
||||
}
|
||||
*entry = new.to_trace_as_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RenameTableUpdate::UpdateForWriteL2Reg { dest, src } => {
|
||||
let new = #[hdl(sim)]
|
||||
RenameTableEntry::<_> {
|
||||
l1: #[hdl(sim)]
|
||||
(HdlOption[src.ty()]).HdlNone(),
|
||||
l2: #[hdl(sim)]
|
||||
HdlSome(dest),
|
||||
};
|
||||
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(l1) = &entry.inner().l1 {
|
||||
if l1 == src {
|
||||
println!(
|
||||
"{rename_table_name}: UpdateForWriteL2Reg: {unrenamed_reg_num:#x} \
|
||||
updating from {entry:?} to {new:?}",
|
||||
);
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(_) = &entry.inner().l2 {
|
||||
unreachable!("l2 should be HdlNone: {entry:?}");
|
||||
}
|
||||
*entry = new.to_trace_as_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RenameTableUpdate::DropAllL2RegFileOutputs => {
|
||||
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(_) = &entry.inner().l1 {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(_) = &entry.inner().l2 {
|
||||
let mut new = entry.inner().clone();
|
||||
new.l1 = #[hdl(sim)]
|
||||
(new.l1.ty()).HdlNone();
|
||||
println!(
|
||||
"{rename_table_name}: DropAllL2RegFileOutputs: {unrenamed_reg_num:#x} \
|
||||
updating from {entry:?} to {new:?}",
|
||||
);
|
||||
*entry = new.to_trace_as_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[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)]
|
||||
if let HdlSome(v) = &entry.inner().l1 {
|
||||
if UnitNum::index_sim(&v.unit_num) == Some(unit_index) {
|
||||
seen.insert(UnitOutRegNum::value_sim(&v.unit_out_reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
seen.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default)]
|
||||
enum MOpInUnitState {
|
||||
#[default]
|
||||
|
|
@ -631,176 +447,6 @@ impl fmt::Debug for MOpInUnitState {
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
type SimOnlyMOpInUnitState = SimOnly<MOpInUnitState>;
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
/// See [`RobEntry::is_register_fence`]
|
||||
is_register_fence: Bool,
|
||||
/// See [`RobEntry::is_register_fence`]
|
||||
done_waiting_for_register_fences: Bool,
|
||||
mop: MOpInstance<RenamedMOp<C>>,
|
||||
unit_index: UIntInRangeType<ConstUsize<0>, CpuConfigUnitCount<C>>,
|
||||
mop_in_unit_state: SimOnlyMOpInUnitState,
|
||||
is_speculative: Bool,
|
||||
all_prior_mops_finished_and_or_caused_cancel: Bool,
|
||||
output: HdlOption<NextPcPredictorOp<C>>,
|
||||
caused_cancel: HdlOption<UnitCausedCancel<C>>,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
mop,
|
||||
is_register_fence: _,
|
||||
done_waiting_for_register_fences: _,
|
||||
unit_index,
|
||||
mop_in_unit_state: _,
|
||||
is_speculative: _,
|
||||
all_prior_mops_finished_and_or_caused_cancel: _,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
mop: zeroed(mop),
|
||||
is_register_fence: false,
|
||||
done_waiting_for_register_fences: false,
|
||||
unit_index: zeroed(unit_index),
|
||||
mop_in_unit_state: SimOnlyValue::default(),
|
||||
is_speculative: false,
|
||||
all_prior_mops_finished_and_or_caused_cancel: false,
|
||||
output: #[hdl(sim)]
|
||||
output.HdlNone(),
|
||||
caused_cancel: #[hdl(sim)]
|
||||
caused_cancel.HdlNone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RobEntry<C: PhantomConstCpuConfig> {
|
||||
/// Block this and all later µOps until all prior µOps are finished and didn't cause a cancel.
|
||||
/// Not to be confused with memory fences.
|
||||
is_register_fence: bool,
|
||||
/// See [`Self::is_register_fence`]
|
||||
done_waiting_for_register_fences: bool,
|
||||
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||
unit_index: usize,
|
||||
mop_in_unit_state: MOpInUnitState,
|
||||
is_speculative: bool,
|
||||
all_prior_mops_finished_and_or_caused_cancel: bool,
|
||||
output: Option<SimValue<NextPcPredictorOp<C>>>,
|
||||
caused_cancel: Option<SimValue<UnitCausedCancel<C>>>,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> RobEntry<C> {
|
||||
fn new(
|
||||
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||
unit_index: usize,
|
||||
is_register_fence: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
mop,
|
||||
is_register_fence,
|
||||
done_waiting_for_register_fences: false,
|
||||
unit_index,
|
||||
mop_in_unit_state: MOpInUnitState::NotYetEnqueued,
|
||||
is_speculative: true,
|
||||
all_prior_mops_finished_and_or_caused_cancel: false,
|
||||
output: None,
|
||||
caused_cancel: None,
|
||||
}
|
||||
}
|
||||
fn dest_reg(&self) -> Option<&SimValue<PRegNum<C>>> {
|
||||
let dest_reg = MOpTrait::dest_reg_sim_ref(self.mop.mop.inner());
|
||||
let unit_index = UnitNum::index_sim(&dest_reg.unit_num)?;
|
||||
assert_eq!(unit_index, self.unit_index);
|
||||
Some(dest_reg)
|
||||
}
|
||||
fn unit_out_reg(&self) -> Option<&SimValue<UnitOutRegNum<C>>> {
|
||||
Some(&self.dest_reg()?.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 {
|
||||
is_register_fence,
|
||||
done_waiting_for_register_fences,
|
||||
mop,
|
||||
unit_index,
|
||||
mop_in_unit_state,
|
||||
is_speculative,
|
||||
all_prior_mops_finished_and_or_caused_cancel,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = self;
|
||||
let ret_ty = RobEntryDebugState[config];
|
||||
#[hdl(sim)]
|
||||
RobEntryDebugState::<C> {
|
||||
is_register_fence,
|
||||
done_waiting_for_register_fences,
|
||||
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_finished_and_or_caused_cancel,
|
||||
output: output.into_sim_value_with_type(ret_ty.output),
|
||||
caused_cancel: caused_cancel.into_sim_value_with_type(ret_ty.caused_cancel),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
struct RobEntriesDebugState {
|
||||
unrenamed: MOpInstance<MOp>,
|
||||
/// number of renamed µOps that this unrenamed µOp corresponds to
|
||||
renamed_entries_len: UInt<8>,
|
||||
}
|
||||
|
||||
impl SimValueDefault for RobEntriesDebugState {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
unrenamed,
|
||||
renamed_entries_len,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
unrenamed: zeroed(unrenamed),
|
||||
renamed_entries_len: renamed_entries_len.sim_value_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RobEntries<C: PhantomConstCpuConfig> {
|
||||
unrenamed: SimValue<MOpInstance<MOp>>,
|
||||
rename_table_updates: Vec<RenameTableUpdate<C>>,
|
||||
renamed_entries: VecDeque<RobEntry<C>>,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> RobEntries<C> {
|
||||
#[hdl]
|
||||
fn debug_state(&self) -> SimValue<RobEntriesDebugState> {
|
||||
let Self {
|
||||
unrenamed,
|
||||
rename_table_updates: _,
|
||||
renamed_entries,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
RobEntriesDebugState {
|
||||
unrenamed,
|
||||
renamed_entries_len: u8::try_from(renamed_entries.len())
|
||||
.expect("renamed_entries.len() should fit in u8"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct NeedSendCancelDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
send_to_next_pc: HdlOption<UInt<64>>,
|
||||
|
|
@ -849,240 +495,6 @@ impl NextPcCancelingState {
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
pub struct ReorderBufferDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
next_renamed_mop_id: MOpId,
|
||||
entries: ArrayVec<RobEntriesDebugState, CpuConfigRobSize<C>>,
|
||||
incomplete_back_entry: HdlOption<RobEntriesDebugState>,
|
||||
renamed: ArrayVec<RobEntryDebugState<C>, CpuConfigRobSize<C>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> SimValueDefault for ReorderBufferDebugState<C> {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
next_renamed_mop_id,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
renamed,
|
||||
config,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
next_renamed_mop_id: next_renamed_mop_id.sim_value_default(),
|
||||
entries: entries.sim_value_default(),
|
||||
incomplete_back_entry: incomplete_back_entry.sim_value_default(),
|
||||
renamed: renamed.sim_value_default(),
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReorderBuffer<C: PhantomConstCpuConfig> {
|
||||
next_renamed_mop_id: SimValue<MOpId>,
|
||||
entries: VecDeque<RobEntries<C>>,
|
||||
incomplete_back_entry: Option<RobEntries<C>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> ReorderBuffer<C> {
|
||||
fn new(config: C) -> Self {
|
||||
Self {
|
||||
next_renamed_mop_id: MOpId.zero().into_sim_value(),
|
||||
entries: VecDeque::new(),
|
||||
incomplete_back_entry: None,
|
||||
config,
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
fn debug_state(&self) -> SimValue<ReorderBufferDebugState<C>> {
|
||||
let Self {
|
||||
next_renamed_mop_id,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config,
|
||||
} = self;
|
||||
let ty = ReorderBufferDebugState[*config];
|
||||
#[hdl(sim)]
|
||||
ReorderBufferDebugState::<_> {
|
||||
next_renamed_mop_id,
|
||||
entries: ty
|
||||
.entries
|
||||
.from_iter_sim(
|
||||
zeroed(StaticType::TYPE),
|
||||
entries.iter().map(RobEntries::debug_state),
|
||||
)
|
||||
.expect("known to fit"),
|
||||
incomplete_back_entry: incomplete_back_entry.as_ref().map(|v| v.debug_state()),
|
||||
renamed: ty
|
||||
.renamed
|
||||
.from_iter_sim(
|
||||
ty.renamed.element().sim_value_default(),
|
||||
self.renamed().map(|v| v.debug_state(*config)),
|
||||
)
|
||||
.ok()
|
||||
.expect("known to fit"),
|
||||
config,
|
||||
}
|
||||
}
|
||||
fn unrenamed_len(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
fn unrenamed(&self) -> impl DoubleEndedIterator<Item = &SimValue<MOpInstance<MOp>>> + Clone {
|
||||
self.entries.iter().map(|v| &v.unrenamed)
|
||||
}
|
||||
fn unrenamed_mut(
|
||||
&mut self,
|
||||
) -> impl DoubleEndedIterator<Item = &mut SimValue<MOpInstance<MOp>>> {
|
||||
self.entries.iter_mut().map(|v| &mut v.unrenamed)
|
||||
}
|
||||
fn retire_groups_unrenamed_ranges(
|
||||
&self,
|
||||
) -> impl Clone + Iterator<Item = std::ops::Range<usize>> {
|
||||
let mut next_group_start = 0;
|
||||
self.entries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(move |(index, entry)| {
|
||||
if *entry.unrenamed.is_last_mop_in_insn {
|
||||
let group_start = next_group_start;
|
||||
next_group_start = index + 1;
|
||||
Some(group_start..next_group_start)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
fn retire_groups(
|
||||
&self,
|
||||
) -> impl Clone + Iterator<Item: DoubleEndedIterator<Item = &RobEntries<C>> + Clone> {
|
||||
self.retire_groups_unrenamed_ranges()
|
||||
.map(|range| self.entries.range(range))
|
||||
}
|
||||
fn renamed_len(&self) -> usize {
|
||||
let Self {
|
||||
next_renamed_mop_id: _,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config: _,
|
||||
} = self;
|
||||
entries
|
||||
.iter()
|
||||
.chain(incomplete_back_entry)
|
||||
.map(|entries| entries.renamed_entries.len())
|
||||
.sum()
|
||||
}
|
||||
fn renamed(&self) -> impl DoubleEndedIterator<Item = &RobEntry<C>> + Clone {
|
||||
let Self {
|
||||
next_renamed_mop_id: _,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config: _,
|
||||
} = self;
|
||||
entries
|
||||
.iter()
|
||||
.chain(incomplete_back_entry)
|
||||
.flat_map(|entries| &entries.renamed_entries)
|
||||
}
|
||||
fn renamed_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut RobEntry<C>> {
|
||||
let Self {
|
||||
next_renamed_mop_id: _,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config: _,
|
||||
} = self;
|
||||
entries
|
||||
.iter_mut()
|
||||
.chain(incomplete_back_entry)
|
||||
.flat_map(|entries| &mut entries.renamed_entries)
|
||||
}
|
||||
fn try_renamed_by_id(&self, id: &SimValue<MOpId>) -> Option<&RobEntry<C>> {
|
||||
self.renamed().find(|v| v.mop.id == *id)
|
||||
}
|
||||
fn try_renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> Option<&mut RobEntry<C>> {
|
||||
self.renamed_mut().find(|v| v.mop.id == *id)
|
||||
}
|
||||
#[track_caller]
|
||||
fn renamed_by_id(&self, id: &SimValue<MOpId>) -> &RobEntry<C> {
|
||||
match self.try_renamed_by_id(id) {
|
||||
Some(v) => v,
|
||||
None => panic!("MOpId not found: {id:?}"),
|
||||
}
|
||||
}
|
||||
fn renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> &mut RobEntry<C> {
|
||||
match self.try_renamed_by_id_mut(id) {
|
||||
Some(v) => v,
|
||||
None => panic!("MOpId not found: {id:?}"),
|
||||
}
|
||||
}
|
||||
fn renamed_push_back_with_new_id(
|
||||
&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);
|
||||
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;
|
||||
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
|
||||
.incomplete_back_entry
|
||||
.take()
|
||||
.unwrap_or_else(|| RobEntries {
|
||||
unrenamed: unrenamed.clone(),
|
||||
rename_table_updates: Vec::new(),
|
||||
renamed_entries: VecDeque::new(),
|
||||
});
|
||||
self.entries.push_back(entry);
|
||||
}
|
||||
fn clear(&mut self) {
|
||||
let Self {
|
||||
next_renamed_mop_id: _,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config: _,
|
||||
} = self;
|
||||
entries.clear();
|
||||
*incomplete_back_entry = None;
|
||||
}
|
||||
fn unrenamed_back_append_rename_table_update(
|
||||
&mut self,
|
||||
unrenamed: &SimValue<MOpInstance<MOp>>,
|
||||
update: RenameTableUpdate<C>,
|
||||
) {
|
||||
self.incomplete_back_entry
|
||||
.get_or_insert_with(|| RobEntries {
|
||||
unrenamed: unrenamed.clone(),
|
||||
rename_table_updates: Vec::new(),
|
||||
renamed_entries: VecDeque::new(),
|
||||
})
|
||||
.rename_table_updates
|
||||
.push(update);
|
||||
}
|
||||
fn all_mops_are_finished_and_or_caused_cancel(&self) -> bool {
|
||||
self.renamed().next().is_none_or(|entry| {
|
||||
entry.all_prior_mops_finished_and_or_caused_cancel
|
||||
&& entry.mop_in_unit_state.is_finished_and_or_caused_cancel()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type SimOnlyString = SimOnly<String>;
|
||||
#[expect(non_upper_case_globals)]
|
||||
const SimOnlyString: SimOnlyString = SimOnlyString::TYPE;
|
||||
|
|
@ -1321,9 +733,9 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
}
|
||||
for entry in self
|
||||
.rename_table
|
||||
.entries
|
||||
.entries()
|
||||
.iter()
|
||||
.chain(self.retire_rename_table.entries.iter())
|
||||
.chain(self.retire_rename_table.entries().iter())
|
||||
{
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(l1) = &entry.inner().l1 {
|
||||
|
|
@ -1351,9 +763,9 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
}
|
||||
for entry in self
|
||||
.rename_table
|
||||
.entries
|
||||
.entries()
|
||||
.iter()
|
||||
.chain(self.retire_rename_table.entries.iter())
|
||||
.chain(self.retire_rename_table.entries().iter())
|
||||
{
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(l2) = &entry.inner().l2 {
|
||||
|
|
@ -1422,7 +834,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
});
|
||||
let [src_reg] = src_regs;
|
||||
let renamed_reg =
|
||||
self.rename_table.entries[MOpRegNum::reg_num_sim(&src_reg) as usize].clone();
|
||||
self.rename_table.entries()[MOpRegNum::reg_num_sim(&src_reg) as usize].clone();
|
||||
println!("moving from {src_reg:?} renamed: {renamed_reg:?}");
|
||||
let unrenamed_dest_regs =
|
||||
MOpDestReg::regs_sim(MOpTrait::dest_reg_sim_ref(move_reg_mop));
|
||||
|
|
@ -1537,7 +949,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
let mut any_collisions = false;
|
||||
MOpTrait::for_each_src_reg_sim_ref(insn.mop.inner(), &mut |src_reg, _| {
|
||||
let renamed =
|
||||
&self.rename_table.entries[MOpRegNum::reg_num_sim(&src_reg) as usize];
|
||||
&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"
|
||||
);
|
||||
|
|
@ -1635,7 +1047,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
|
|||
&renamed_dest_reg,
|
||||
PRegNum[self.config],
|
||||
&mut |src_reg, index| {
|
||||
let renamed = &self.rename_table.entries[MOpRegNum::reg_num_sim(&src_reg) as usize];
|
||||
let renamed =
|
||||
&self.rename_table.entries()[MOpRegNum::reg_num_sim(&src_reg) as usize];
|
||||
println!("renaming src[{index}] from {src_reg:?} to {renamed:?}");
|
||||
#[hdl(sim)]
|
||||
match &renamed.inner().l1 {
|
||||
|
|
|
|||
200
crates/cpu/src/rename_execute_retire/rename_table.rs
Normal file
200
crates/cpu/src/rename_execute_retire/rename_table.rs
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
config::{CpuConfig, PhantomConstCpuConfig},
|
||||
instruction::{L2RegNum, MOpRegNum, PRegNum, UnitNum, UnitOutRegNum},
|
||||
};
|
||||
use fayalite::prelude::*;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[hdl(no_static)]
|
||||
pub(crate) struct RenameTableEntry<C: PhantomConstGet<CpuConfig>> {
|
||||
pub(crate) l1: HdlOption<PRegNum<C>>,
|
||||
pub(crate) l2: HdlOption<L2RegNum>,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> RenameTableEntry<C> {
|
||||
#[hdl]
|
||||
pub(crate) fn const_zero(self) -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
l1: #[hdl(sim)]
|
||||
(self.l1).HdlSome(self.l1.HdlSome.const_zero()),
|
||||
l2: #[hdl(sim)]
|
||||
HdlNone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// make arrays dynamically-sized to avoid putting large types on the stack
|
||||
#[hdl(get(|c| 1 << MOpRegNum::WIDTH))]
|
||||
type MOpRegCount<C: PhantomConstGet<CpuConfig>> = DynSize;
|
||||
|
||||
#[hdl(no_static)]
|
||||
pub(crate) struct RenameTableDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
entries: ArrayType<TraceAsString<RenameTableEntry<C>>, MOpRegCount<C>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RenameTable<C: PhantomConstCpuConfig> {
|
||||
entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> Clone for RenameTable<C> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
entries: self.entries.clone(),
|
||||
config: self.config.clone(),
|
||||
}
|
||||
}
|
||||
fn clone_from(&mut self, source: &Self) {
|
||||
let Self { entries, config } = self;
|
||||
entries.clone_from(&source.entries);
|
||||
*config = source.config;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum RenameTableUpdate<C: PhantomConstCpuConfig> {
|
||||
Write {
|
||||
unrenamed_reg_num: u32,
|
||||
new: SimValue<TraceAsString<RenameTableEntry<C>>>,
|
||||
},
|
||||
UpdateForReadL2Reg {
|
||||
dest: SimValue<PRegNum<C>>,
|
||||
src: SimValue<L2RegNum>,
|
||||
},
|
||||
UpdateForWriteL2Reg {
|
||||
dest: SimValue<L2RegNum>,
|
||||
src: SimValue<PRegNum<C>>,
|
||||
},
|
||||
DropAllL2RegFileOutputs,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> RenameTable<C> {
|
||||
pub(crate) fn new(config: C) -> Self {
|
||||
let entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]> =
|
||||
vec![
|
||||
RenameTableEntry[config].const_zero().into_trace_as_string();
|
||||
1 << MOpRegNum::WIDTH
|
||||
]
|
||||
.try_into()
|
||||
.expect("size is known to match");
|
||||
Self { entries, config }
|
||||
}
|
||||
pub(crate) fn entries(
|
||||
&self,
|
||||
) -> &[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH] {
|
||||
&self.entries
|
||||
}
|
||||
#[hdl]
|
||||
pub(crate) fn to_debug_state(&self) -> SimValue<RenameTableDebugState<C>> {
|
||||
let Self { entries, config } = self;
|
||||
let ty = RenameTableDebugState[*config];
|
||||
#[hdl(sim)]
|
||||
RenameTableDebugState::<_> {
|
||||
entries: entries.to_sim_value_with_type(ty.entries),
|
||||
config,
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
pub(crate) fn update(&mut self, update: &RenameTableUpdate<C>, rename_table_name: &str) {
|
||||
match update {
|
||||
RenameTableUpdate::Write {
|
||||
unrenamed_reg_num,
|
||||
new,
|
||||
} => {
|
||||
if *unrenamed_reg_num == MOpRegNum::CONST_ZERO_REG_NUM {
|
||||
// writing to const zero reg does nothing
|
||||
return;
|
||||
}
|
||||
println!("{rename_table_name}: Write: {unrenamed_reg_num:#x} <- {new:?}");
|
||||
self.entries[*unrenamed_reg_num as usize] = new.clone();
|
||||
}
|
||||
RenameTableUpdate::UpdateForReadL2Reg { dest, src } => {
|
||||
let new = #[hdl(sim)]
|
||||
RenameTableEntry::<_> {
|
||||
l1: #[hdl(sim)]
|
||||
(HdlOption[dest.ty()]).HdlSome(dest),
|
||||
l2: #[hdl(sim)]
|
||||
HdlSome(src),
|
||||
};
|
||||
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(l2) = &entry.inner().l2 {
|
||||
if L2RegNum::value_sim(l2) == L2RegNum::value_sim(src) {
|
||||
println!(
|
||||
"{rename_table_name}: UpdateForReadL2Reg: {unrenamed_reg_num:#x} \
|
||||
updating from {entry:?} to {new:?}",
|
||||
);
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(_) = &entry.inner().l1 {
|
||||
unreachable!("l1 should be HdlNone: {entry:?}");
|
||||
}
|
||||
*entry = new.to_trace_as_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RenameTableUpdate::UpdateForWriteL2Reg { dest, src } => {
|
||||
let new = #[hdl(sim)]
|
||||
RenameTableEntry::<_> {
|
||||
l1: #[hdl(sim)]
|
||||
(HdlOption[src.ty()]).HdlNone(),
|
||||
l2: #[hdl(sim)]
|
||||
HdlSome(dest),
|
||||
};
|
||||
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(l1) = &entry.inner().l1 {
|
||||
if l1 == src {
|
||||
println!(
|
||||
"{rename_table_name}: UpdateForWriteL2Reg: {unrenamed_reg_num:#x} \
|
||||
updating from {entry:?} to {new:?}",
|
||||
);
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(_) = &entry.inner().l2 {
|
||||
unreachable!("l2 should be HdlNone: {entry:?}");
|
||||
}
|
||||
*entry = new.to_trace_as_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RenameTableUpdate::DropAllL2RegFileOutputs => {
|
||||
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(_) = &entry.inner().l1 {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(_) = &entry.inner().l2 {
|
||||
let mut new = entry.inner().clone();
|
||||
new.l1 = #[hdl(sim)]
|
||||
(new.l1.ty()).HdlNone();
|
||||
println!(
|
||||
"{rename_table_name}: DropAllL2RegFileOutputs: {unrenamed_reg_num:#x} \
|
||||
updating from {entry:?} to {new:?}",
|
||||
);
|
||||
*entry = new.to_trace_as_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
pub(crate) fn used_unit_out_reg_count(&self, unit_index: usize) -> usize {
|
||||
let mut seen = BTreeSet::new();
|
||||
for entry in self.entries.iter() {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(v) = &entry.inner().l1 {
|
||||
if UnitNum::index_sim(&v.unit_num) == Some(unit_index) {
|
||||
seen.insert(UnitOutRegNum::value_sim(&v.unit_out_reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
seen.len()
|
||||
}
|
||||
}
|
||||
421
crates/cpu/src/rename_execute_retire/reorder_buffer.rs
Normal file
421
crates/cpu/src/rename_execute_retire/reorder_buffer.rs
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
config::{CpuConfig, CpuConfigRobSize, CpuConfigUnitCount, PhantomConstCpuConfig},
|
||||
instruction::{MOp, MOpTrait, PRegNum, UnitNum, UnitOutRegNum},
|
||||
next_pc::SimValueDefault,
|
||||
rename_execute_retire::{
|
||||
MOpId, MOpInUnitState, MOpInstance, NextPcPredictorOp, RenamedMOp, UnitCausedCancel,
|
||||
rename_table::RenameTableUpdate, zeroed,
|
||||
},
|
||||
util::array_vec::ArrayVec,
|
||||
};
|
||||
use fayalite::{int::UIntInRangeType, prelude::*, ty::StaticType};
|
||||
use std::{collections::VecDeque, mem};
|
||||
|
||||
#[hdl]
|
||||
type SimOnlyMOpInUnitState = SimOnly<MOpInUnitState>;
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
/// See [`RobEntry::is_register_fence`]
|
||||
is_register_fence: Bool,
|
||||
/// See [`RobEntry::is_register_fence`]
|
||||
done_waiting_for_register_fences: Bool,
|
||||
mop: MOpInstance<RenamedMOp<C>>,
|
||||
unit_index: UIntInRangeType<ConstUsize<0>, CpuConfigUnitCount<C>>,
|
||||
mop_in_unit_state: SimOnlyMOpInUnitState,
|
||||
is_speculative: Bool,
|
||||
all_prior_mops_finished_and_or_caused_cancel: Bool,
|
||||
output: HdlOption<NextPcPredictorOp<C>>,
|
||||
caused_cancel: HdlOption<UnitCausedCancel<C>>,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
mop,
|
||||
is_register_fence: _,
|
||||
done_waiting_for_register_fences: _,
|
||||
unit_index,
|
||||
mop_in_unit_state: _,
|
||||
is_speculative: _,
|
||||
all_prior_mops_finished_and_or_caused_cancel: _,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
mop: zeroed(mop),
|
||||
is_register_fence: false,
|
||||
done_waiting_for_register_fences: false,
|
||||
unit_index: zeroed(unit_index),
|
||||
mop_in_unit_state: SimOnlyValue::default(),
|
||||
is_speculative: false,
|
||||
all_prior_mops_finished_and_or_caused_cancel: false,
|
||||
output: #[hdl(sim)]
|
||||
output.HdlNone(),
|
||||
caused_cancel: #[hdl(sim)]
|
||||
caused_cancel.HdlNone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RobEntry<C: PhantomConstCpuConfig> {
|
||||
/// Block this and all later µOps until all prior µOps are finished and didn't cause a cancel.
|
||||
/// Not to be confused with memory fences.
|
||||
pub(crate) is_register_fence: bool,
|
||||
/// See [`Self::is_register_fence`]
|
||||
pub(crate) done_waiting_for_register_fences: bool,
|
||||
pub(crate) mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||
pub(crate) unit_index: usize,
|
||||
pub(crate) mop_in_unit_state: MOpInUnitState,
|
||||
pub(crate) is_speculative: bool,
|
||||
pub(crate) all_prior_mops_finished_and_or_caused_cancel: bool,
|
||||
pub(crate) output: Option<SimValue<NextPcPredictorOp<C>>>,
|
||||
pub(crate) caused_cancel: Option<SimValue<UnitCausedCancel<C>>>,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> RobEntry<C> {
|
||||
pub(crate) fn new(
|
||||
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
|
||||
unit_index: usize,
|
||||
is_register_fence: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
mop,
|
||||
is_register_fence,
|
||||
done_waiting_for_register_fences: false,
|
||||
unit_index,
|
||||
mop_in_unit_state: MOpInUnitState::NotYetEnqueued,
|
||||
is_speculative: true,
|
||||
all_prior_mops_finished_and_or_caused_cancel: false,
|
||||
output: None,
|
||||
caused_cancel: None,
|
||||
}
|
||||
}
|
||||
fn dest_reg(&self) -> Option<&SimValue<PRegNum<C>>> {
|
||||
let dest_reg = MOpTrait::dest_reg_sim_ref(self.mop.mop.inner());
|
||||
let unit_index = UnitNum::index_sim(&dest_reg.unit_num)?;
|
||||
assert_eq!(unit_index, self.unit_index);
|
||||
Some(dest_reg)
|
||||
}
|
||||
fn unit_out_reg(&self) -> Option<&SimValue<UnitOutRegNum<C>>> {
|
||||
Some(&self.dest_reg()?.unit_out_reg)
|
||||
}
|
||||
pub(crate) 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 {
|
||||
is_register_fence,
|
||||
done_waiting_for_register_fences,
|
||||
mop,
|
||||
unit_index,
|
||||
mop_in_unit_state,
|
||||
is_speculative,
|
||||
all_prior_mops_finished_and_or_caused_cancel,
|
||||
output,
|
||||
caused_cancel,
|
||||
} = self;
|
||||
let ret_ty = RobEntryDebugState[config];
|
||||
#[hdl(sim)]
|
||||
RobEntryDebugState::<C> {
|
||||
is_register_fence,
|
||||
done_waiting_for_register_fences,
|
||||
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_finished_and_or_caused_cancel,
|
||||
output: output.into_sim_value_with_type(ret_ty.output),
|
||||
caused_cancel: caused_cancel.into_sim_value_with_type(ret_ty.caused_cancel),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
struct RobEntriesDebugState {
|
||||
unrenamed: MOpInstance<MOp>,
|
||||
/// number of renamed µOps that this unrenamed µOp corresponds to
|
||||
renamed_entries_len: UInt<8>,
|
||||
}
|
||||
|
||||
impl SimValueDefault for RobEntriesDebugState {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
unrenamed,
|
||||
renamed_entries_len,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
unrenamed: zeroed(unrenamed),
|
||||
renamed_entries_len: renamed_entries_len.sim_value_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RobEntries<C: PhantomConstCpuConfig> {
|
||||
pub(crate) unrenamed: SimValue<MOpInstance<MOp>>,
|
||||
pub(crate) rename_table_updates: Vec<RenameTableUpdate<C>>,
|
||||
pub(crate) renamed_entries: VecDeque<RobEntry<C>>,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> RobEntries<C> {
|
||||
#[hdl]
|
||||
fn debug_state(&self) -> SimValue<RobEntriesDebugState> {
|
||||
let Self {
|
||||
unrenamed,
|
||||
rename_table_updates: _,
|
||||
renamed_entries,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
RobEntriesDebugState {
|
||||
unrenamed,
|
||||
renamed_entries_len: u8::try_from(renamed_entries.len())
|
||||
.expect("renamed_entries.len() should fit in u8"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
pub(crate) struct ReorderBufferDebugState<C: PhantomConstGet<CpuConfig>> {
|
||||
next_renamed_mop_id: MOpId,
|
||||
entries: ArrayVec<RobEntriesDebugState, CpuConfigRobSize<C>>,
|
||||
incomplete_back_entry: HdlOption<RobEntriesDebugState>,
|
||||
renamed: ArrayVec<RobEntryDebugState<C>, CpuConfigRobSize<C>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> SimValueDefault for ReorderBufferDebugState<C> {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
next_renamed_mop_id,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
renamed,
|
||||
config,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
next_renamed_mop_id: next_renamed_mop_id.sim_value_default(),
|
||||
entries: entries.sim_value_default(),
|
||||
incomplete_back_entry: incomplete_back_entry.sim_value_default(),
|
||||
renamed: renamed.sim_value_default(),
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ReorderBuffer<C: PhantomConstCpuConfig> {
|
||||
next_renamed_mop_id: SimValue<MOpId>,
|
||||
pub(crate) entries: VecDeque<RobEntries<C>>,
|
||||
pub(crate) incomplete_back_entry: Option<RobEntries<C>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: PhantomConstCpuConfig> ReorderBuffer<C> {
|
||||
pub(crate) fn new(config: C) -> Self {
|
||||
Self {
|
||||
next_renamed_mop_id: MOpId.zero().into_sim_value(),
|
||||
entries: VecDeque::new(),
|
||||
incomplete_back_entry: None,
|
||||
config,
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
pub(crate) fn debug_state(&self) -> SimValue<ReorderBufferDebugState<C>> {
|
||||
let Self {
|
||||
next_renamed_mop_id,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config,
|
||||
} = self;
|
||||
let ty = ReorderBufferDebugState[*config];
|
||||
#[hdl(sim)]
|
||||
ReorderBufferDebugState::<_> {
|
||||
next_renamed_mop_id,
|
||||
entries: ty
|
||||
.entries
|
||||
.from_iter_sim(
|
||||
zeroed(StaticType::TYPE),
|
||||
entries.iter().map(RobEntries::debug_state),
|
||||
)
|
||||
.expect("known to fit"),
|
||||
incomplete_back_entry: incomplete_back_entry.as_ref().map(|v| v.debug_state()),
|
||||
renamed: ty
|
||||
.renamed
|
||||
.from_iter_sim(
|
||||
ty.renamed.element().sim_value_default(),
|
||||
self.renamed().map(|v| v.debug_state(*config)),
|
||||
)
|
||||
.ok()
|
||||
.expect("known to fit"),
|
||||
config,
|
||||
}
|
||||
}
|
||||
pub(crate) fn unrenamed_len(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
pub(crate) fn unrenamed(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = &SimValue<MOpInstance<MOp>>> + Clone {
|
||||
self.entries.iter().map(|v| &v.unrenamed)
|
||||
}
|
||||
fn unrenamed_mut(
|
||||
&mut self,
|
||||
) -> impl DoubleEndedIterator<Item = &mut SimValue<MOpInstance<MOp>>> {
|
||||
self.entries.iter_mut().map(|v| &mut v.unrenamed)
|
||||
}
|
||||
fn retire_groups_unrenamed_ranges(
|
||||
&self,
|
||||
) -> impl Clone + Iterator<Item = std::ops::Range<usize>> {
|
||||
let mut next_group_start = 0;
|
||||
self.entries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(move |(index, entry)| {
|
||||
if *entry.unrenamed.is_last_mop_in_insn {
|
||||
let group_start = next_group_start;
|
||||
next_group_start = index + 1;
|
||||
Some(group_start..next_group_start)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
pub(crate) fn retire_groups(
|
||||
&self,
|
||||
) -> impl Clone + Iterator<Item: DoubleEndedIterator<Item = &RobEntries<C>> + Clone> {
|
||||
self.retire_groups_unrenamed_ranges()
|
||||
.map(|range| self.entries.range(range))
|
||||
}
|
||||
pub(crate) fn renamed_len(&self) -> usize {
|
||||
let Self {
|
||||
next_renamed_mop_id: _,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config: _,
|
||||
} = self;
|
||||
entries
|
||||
.iter()
|
||||
.chain(incomplete_back_entry)
|
||||
.map(|entries| entries.renamed_entries.len())
|
||||
.sum()
|
||||
}
|
||||
pub(crate) fn renamed(&self) -> impl DoubleEndedIterator<Item = &RobEntry<C>> + Clone {
|
||||
let Self {
|
||||
next_renamed_mop_id: _,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config: _,
|
||||
} = self;
|
||||
entries
|
||||
.iter()
|
||||
.chain(incomplete_back_entry)
|
||||
.flat_map(|entries| &entries.renamed_entries)
|
||||
}
|
||||
pub(crate) fn renamed_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut RobEntry<C>> {
|
||||
let Self {
|
||||
next_renamed_mop_id: _,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config: _,
|
||||
} = self;
|
||||
entries
|
||||
.iter_mut()
|
||||
.chain(incomplete_back_entry)
|
||||
.flat_map(|entries| &mut entries.renamed_entries)
|
||||
}
|
||||
fn try_renamed_by_id(&self, id: &SimValue<MOpId>) -> Option<&RobEntry<C>> {
|
||||
self.renamed().find(|v| v.mop.id == *id)
|
||||
}
|
||||
fn try_renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> Option<&mut RobEntry<C>> {
|
||||
self.renamed_mut().find(|v| v.mop.id == *id)
|
||||
}
|
||||
#[track_caller]
|
||||
fn renamed_by_id(&self, id: &SimValue<MOpId>) -> &RobEntry<C> {
|
||||
match self.try_renamed_by_id(id) {
|
||||
Some(v) => v,
|
||||
None => panic!("MOpId not found: {id:?}"),
|
||||
}
|
||||
}
|
||||
pub(crate) fn renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> &mut RobEntry<C> {
|
||||
match self.try_renamed_by_id_mut(id) {
|
||||
Some(v) => v,
|
||||
None => panic!("MOpId not found: {id:?}"),
|
||||
}
|
||||
}
|
||||
pub(crate) fn renamed_push_back_with_new_id(
|
||||
&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);
|
||||
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;
|
||||
renamed_entries.push_back(renamed);
|
||||
&renamed_entries.back().expect("just pushed").mop.id
|
||||
}
|
||||
pub(crate) fn finished_unrenamed_push_back(&mut self, unrenamed: &SimValue<MOpInstance<MOp>>) {
|
||||
let entry = self
|
||||
.incomplete_back_entry
|
||||
.take()
|
||||
.unwrap_or_else(|| RobEntries {
|
||||
unrenamed: unrenamed.clone(),
|
||||
rename_table_updates: Vec::new(),
|
||||
renamed_entries: VecDeque::new(),
|
||||
});
|
||||
self.entries.push_back(entry);
|
||||
}
|
||||
pub(crate) fn clear(&mut self) {
|
||||
let Self {
|
||||
next_renamed_mop_id: _,
|
||||
entries,
|
||||
incomplete_back_entry,
|
||||
config: _,
|
||||
} = self;
|
||||
entries.clear();
|
||||
*incomplete_back_entry = None;
|
||||
}
|
||||
pub(crate) fn unrenamed_back_append_rename_table_update(
|
||||
&mut self,
|
||||
unrenamed: &SimValue<MOpInstance<MOp>>,
|
||||
update: RenameTableUpdate<C>,
|
||||
) {
|
||||
self.incomplete_back_entry
|
||||
.get_or_insert_with(|| RobEntries {
|
||||
unrenamed: unrenamed.clone(),
|
||||
rename_table_updates: Vec::new(),
|
||||
renamed_entries: VecDeque::new(),
|
||||
})
|
||||
.rename_table_updates
|
||||
.push(update);
|
||||
}
|
||||
pub(crate) fn all_mops_are_finished_and_or_caused_cancel(&self) -> bool {
|
||||
self.renamed().next().is_none_or(|entry| {
|
||||
entry.all_prior_mops_finished_and_or_caused_cancel
|
||||
&& entry.mop_in_unit_state.is_finished_and_or_caused_cancel()
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue