From fdf1e97e10b85ea16c76b5190a46e37682c4c587 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 21 May 2026 20:46:19 -0700 Subject: [PATCH] move RenameTable and ReorderBuffer into their own mods --- crates/cpu/src/rename_execute_retire.rs | 621 +----------------- .../src/rename_execute_retire/rename_table.rs | 200 ++++++ .../rename_execute_retire/reorder_buffer.rs | 421 ++++++++++++ 3 files changed, 638 insertions(+), 604 deletions(-) create mode 100644 crates/cpu/src/rename_execute_retire/rename_table.rs create mode 100644 crates/cpu/src/rename_execute_retire/reorder_buffer.rs diff --git a/crates/cpu/src/rename_execute_retire.rs b/crates/cpu/src/rename_execute_retire.rs index 869f36a..b13338d 100644 --- a/crates/cpu/src/rename_execute_retire.rs +++ b/crates/cpu/src/rename_execute_retire.rs @@ -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 SimValueDefault for RenameExecuteRetireDebugState } } -#[hdl(no_static)] -struct RenameTableEntry> { - l1: HdlOption>, - l2: HdlOption, -} - -impl RenameTableEntry { - #[hdl] - fn const_zero(self) -> SimValue { - #[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> = DynSize; - -#[hdl(no_static)] -struct RenameTableDebugState> { - entries: ArrayType>, MOpRegCount>, - config: C, -} - -#[derive(Debug)] -struct RenameTable { - entries: Box<[SimValue>>; 1 << MOpRegNum::WIDTH]>, - config: C, -} - -impl Clone for RenameTable { - 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 { - Write { - unrenamed_reg_num: u32, - new: SimValue>>, - }, - UpdateForReadL2Reg { - dest: SimValue>, - src: SimValue, - }, - UpdateForWriteL2Reg { - dest: SimValue, - src: SimValue>, - }, - DropAllL2RegFileOutputs, -} - -impl RenameTable { - fn new(config: C) -> Self { - let entries: Box<[SimValue>>; 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> { - 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, 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; - -#[hdl(no_static)] -struct RobEntryDebugState> { - /// See [`RobEntry::is_register_fence`] - is_register_fence: Bool, - /// See [`RobEntry::is_register_fence`] - done_waiting_for_register_fences: Bool, - mop: MOpInstance>, - unit_index: UIntInRangeType, CpuConfigUnitCount>, - mop_in_unit_state: SimOnlyMOpInUnitState, - is_speculative: Bool, - all_prior_mops_finished_and_or_caused_cancel: Bool, - output: HdlOption>, - caused_cancel: HdlOption>, -} - -impl SimValueDefault for RobEntryDebugState { - #[hdl] - fn sim_value_default(self) -> SimValue { - 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 { - /// 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>>, - unit_index: usize, - mop_in_unit_state: MOpInUnitState, - is_speculative: bool, - all_prior_mops_finished_and_or_caused_cancel: bool, - output: Option>>, - caused_cancel: Option>>, -} - -impl RobEntry { - fn new( - mop: SimValue>>, - 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>> { - 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>> { - Some(&self.dest_reg()?.unit_out_reg) - } - fn unit_out_reg_index(&self) -> Option { - Some(UnitOutRegNum::value_sim(self.unit_out_reg()?)) - } - #[hdl] - fn debug_state(&self, config: C) -> SimValue> { - 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:: { - 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, - /// 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 { - 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 { - unrenamed: SimValue>, - rename_table_updates: Vec>, - renamed_entries: VecDeque>, -} - -impl RobEntries { - #[hdl] - fn debug_state(&self) -> SimValue { - 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> { send_to_next_pc: HdlOption>, @@ -849,240 +495,6 @@ impl NextPcCancelingState { } } -#[hdl(no_static)] -pub struct ReorderBufferDebugState> { - next_renamed_mop_id: MOpId, - entries: ArrayVec>, - incomplete_back_entry: HdlOption, - renamed: ArrayVec, CpuConfigRobSize>, - config: C, -} - -impl SimValueDefault for ReorderBufferDebugState { - #[hdl] - fn sim_value_default(self) -> SimValue { - 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 { - next_renamed_mop_id: SimValue, - entries: VecDeque>, - incomplete_back_entry: Option>, - config: C, -} - -impl ReorderBuffer { - 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> { - 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>> + Clone { - self.entries.iter().map(|v| &v.unrenamed) - } - fn unrenamed_mut( - &mut self, - ) -> impl DoubleEndedIterator>> { - self.entries.iter_mut().map(|v| &mut v.unrenamed) - } - fn retire_groups_unrenamed_ranges( - &self, - ) -> impl Clone + Iterator> { - 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> + 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> + 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> { - 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) -> Option<&RobEntry> { - self.renamed().find(|v| v.mop.id == *id) - } - fn try_renamed_by_id_mut(&mut self, id: &SimValue) -> Option<&mut RobEntry> { - self.renamed_mut().find(|v| v.mop.id == *id) - } - #[track_caller] - fn renamed_by_id(&self, id: &SimValue) -> &RobEntry { - 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) -> &mut RobEntry { - 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>, - mut renamed: RobEntry, - ) -> &SimValue { - 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>) { - 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>, - update: RenameTableUpdate, - ) { - 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; #[expect(non_upper_case_globals)] const SimOnlyString: SimOnlyString = SimOnlyString::TYPE; @@ -1321,9 +733,9 @@ impl RenameExecuteRetireState { } 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 RenameExecuteRetireState { } 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 RenameExecuteRetireState { }); 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 RenameExecuteRetireState { 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 RenameExecuteRetireState { &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 { diff --git a/crates/cpu/src/rename_execute_retire/rename_table.rs b/crates/cpu/src/rename_execute_retire/rename_table.rs new file mode 100644 index 0000000..2e40c70 --- /dev/null +++ b/crates/cpu/src/rename_execute_retire/rename_table.rs @@ -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> { + pub(crate) l1: HdlOption>, + pub(crate) l2: HdlOption, +} + +impl RenameTableEntry { + #[hdl] + pub(crate) fn const_zero(self) -> SimValue { + #[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> = DynSize; + +#[hdl(no_static)] +pub(crate) struct RenameTableDebugState> { + entries: ArrayType>, MOpRegCount>, + config: C, +} + +#[derive(Debug)] +pub(crate) struct RenameTable { + entries: Box<[SimValue>>; 1 << MOpRegNum::WIDTH]>, + config: C, +} + +impl Clone for RenameTable { + 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 { + Write { + unrenamed_reg_num: u32, + new: SimValue>>, + }, + UpdateForReadL2Reg { + dest: SimValue>, + src: SimValue, + }, + UpdateForWriteL2Reg { + dest: SimValue, + src: SimValue>, + }, + DropAllL2RegFileOutputs, +} + +impl RenameTable { + pub(crate) fn new(config: C) -> Self { + let entries: Box<[SimValue>>; 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>>; 1 << MOpRegNum::WIDTH] { + &self.entries + } + #[hdl] + pub(crate) fn to_debug_state(&self) -> SimValue> { + 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, 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() + } +} diff --git a/crates/cpu/src/rename_execute_retire/reorder_buffer.rs b/crates/cpu/src/rename_execute_retire/reorder_buffer.rs new file mode 100644 index 0000000..af6469f --- /dev/null +++ b/crates/cpu/src/rename_execute_retire/reorder_buffer.rs @@ -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; + +#[hdl(no_static)] +struct RobEntryDebugState> { + /// See [`RobEntry::is_register_fence`] + is_register_fence: Bool, + /// See [`RobEntry::is_register_fence`] + done_waiting_for_register_fences: Bool, + mop: MOpInstance>, + unit_index: UIntInRangeType, CpuConfigUnitCount>, + mop_in_unit_state: SimOnlyMOpInUnitState, + is_speculative: Bool, + all_prior_mops_finished_and_or_caused_cancel: Bool, + output: HdlOption>, + caused_cancel: HdlOption>, +} + +impl SimValueDefault for RobEntryDebugState { + #[hdl] + fn sim_value_default(self) -> SimValue { + 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 { + /// 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>>, + 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>>, + pub(crate) caused_cancel: Option>>, +} + +impl RobEntry { + pub(crate) fn new( + mop: SimValue>>, + 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>> { + 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>> { + Some(&self.dest_reg()?.unit_out_reg) + } + pub(crate) fn unit_out_reg_index(&self) -> Option { + Some(UnitOutRegNum::value_sim(self.unit_out_reg()?)) + } + #[hdl] + fn debug_state(&self, config: C) -> SimValue> { + 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:: { + 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, + /// 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 { + 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 { + pub(crate) unrenamed: SimValue>, + pub(crate) rename_table_updates: Vec>, + pub(crate) renamed_entries: VecDeque>, +} + +impl RobEntries { + #[hdl] + fn debug_state(&self) -> SimValue { + 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> { + next_renamed_mop_id: MOpId, + entries: ArrayVec>, + incomplete_back_entry: HdlOption, + renamed: ArrayVec, CpuConfigRobSize>, + config: C, +} + +impl SimValueDefault for ReorderBufferDebugState { + #[hdl] + fn sim_value_default(self) -> SimValue { + 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 { + next_renamed_mop_id: SimValue, + pub(crate) entries: VecDeque>, + pub(crate) incomplete_back_entry: Option>, + config: C, +} + +impl ReorderBuffer { + 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> { + 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>> + Clone { + self.entries.iter().map(|v| &v.unrenamed) + } + fn unrenamed_mut( + &mut self, + ) -> impl DoubleEndedIterator>> { + self.entries.iter_mut().map(|v| &mut v.unrenamed) + } + fn retire_groups_unrenamed_ranges( + &self, + ) -> impl Clone + Iterator> { + 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> + 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> + 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> { + 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) -> Option<&RobEntry> { + self.renamed().find(|v| v.mop.id == *id) + } + fn try_renamed_by_id_mut(&mut self, id: &SimValue) -> Option<&mut RobEntry> { + self.renamed_mut().find(|v| v.mop.id == *id) + } + #[track_caller] + fn renamed_by_id(&self, id: &SimValue) -> &RobEntry { + 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) -> &mut RobEntry { + 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>, + mut renamed: RobEntry, + ) -> &SimValue { + 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>) { + 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>, + update: RenameTableUpdate, + ) { + 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() + }) + } +}