From 7038be9268febe90cbabe655884fba0a6fc4e46d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 31 Jul 2026 16:51:04 -0700 Subject: [PATCH] WIP: adding ExecuteToUnitInterface::is_no_longer_speculative_prediction --- crates/cpu/src/rename_execute_retire.rs | 92 ++++++++++++++----- .../rename_execute_retire/reorder_buffer.rs | 15 +++ .../src/test/decode_and_run_single_insn.rs | 10 ++ crates/cpu/src/unit/alu_branch.rs | 1 + crates/cpu/src/unit/load_store.rs | 12 +++ crates/cpu/tests/load_store_unit.rs | 27 ++++++ 6 files changed, 133 insertions(+), 24 deletions(-) diff --git a/crates/cpu/src/rename_execute_retire.rs b/crates/cpu/src/rename_execute_retire.rs index 7258d70..c9f3535 100644 --- a/crates/cpu/src/rename_execute_retire.rs +++ b/crates/cpu/src/rename_execute_retire.rs @@ -203,6 +203,16 @@ pub struct UnitMOpCantCauseCancel> { pub config: C, } +/// This tells the Unit that if [`Self::if_cant_cause_cancel_id`] can no longer cause a cancel +/// (either via [`UnitMOpCantCauseCancel`] or [`UnitFinishCauseCancel`] without a cancel), +/// then [`Self::then_no_longer_speculative_id`] will no longer be speculative. +#[hdl(no_static)] +pub struct UnitMOpIsNoLongerSpeculativePrediction> { + pub if_cant_cause_cancel_id: MOpId, + pub then_no_longer_speculative_id: MOpId, + pub config: C, +} + /// Interface from the Rename/Execute/Retire control logic to a single Unit. /// /// ## State diagram for a single µOp in a Unit @@ -223,6 +233,8 @@ pub struct ExecuteToUnitInterface> { pub inputs_ready: HdlOption>, /// if [`Self::unit_outputs_ready`] is `false`, then this is always [`HdlNone`] pub is_no_longer_speculative: HdlOption>, + /// if [`Self::unit_outputs_ready`] is `false`, then this is always [`HdlNone`] + pub is_no_longer_speculative_prediction: HdlOption>, /// this uses [`Self::unit_outputs_ready`] as a shared ready flag #[hdl(flip)] pub cant_cause_cancel: HdlOption>, @@ -1263,31 +1275,62 @@ impl RenameExecuteRetireState { fn get_unit_mop_is_no_longer_speculative( &self, unit_index: usize, - ) -> SimValue>> { - let ret_ty = HdlOption[UnitMOpIsNoLongerSpeculative[self.config]]; + ) -> Option>> { if self.is_canceling() { - let retval = #[hdl(sim)] - ret_ty.HdlNone(); - return retval; // separate variable to work around rust-analyzer parse error + return None; } for rob in self.rob.renamed() { if rob.unit_index == unit_index && !rob.is_speculative && let Some(_) = rob.mop_in_unit_state.without_speculative() { - let retval = #[hdl(sim)] - ret_ty.HdlSome( + return Some( #[hdl(sim)] UnitMOpIsNoLongerSpeculative::<_> { id: &rob.mop.id, config: self.config, }, ); - return retval; // separate variable to work around rust-analyzer parse error } } - #[hdl(sim)] - ret_ty.HdlNone() + None + } + #[hdl] + fn get_unit_mop_is_no_longer_speculative_prediction( + &self, + unit_index: usize, + ) -> Option>> { + if self.is_canceling() { + return None; + } + let mut renamed_iter = self.rob.renamed(); + let if_cant_cause_cancel_id = loop { + let Some(renamed) = renamed_iter.next() else { + return None; + }; + if renamed.can_cause_cancel() { + if renamed.unit_index != unit_index { + return None; + } + break &renamed.mop.id; + } + }; + for renamed in renamed_iter { + if renamed.unit_index == unit_index { + return Some( + #[hdl(sim)] + UnitMOpIsNoLongerSpeculativePrediction::<_> { + if_cant_cause_cancel_id, + then_no_longer_speculative_id: &renamed.mop.id, + config: self.config, + }, + ); + } + if renamed.can_cause_cancel() { + break; + } + } + None } #[hdl] fn unit_output_ready(&mut self, output_ready: SimValue>) { @@ -1618,21 +1661,8 @@ impl RenameExecuteRetireState { }; } for renamed in self.rob.renamed_mut() { - let can_cause_cancel = match renamed.mop_in_unit_state { - MOpInUnitState::NotYetEnqueued => true, - MOpInUnitState::InputsNotReadySpeculative { can_cause_cancel } => can_cause_cancel, - MOpInUnitState::InputsReady { - speculative: _, - can_cause_cancel, - } => can_cause_cancel, - MOpInUnitState::OutputReady { - speculative: _, - can_cause_cancel, - } => can_cause_cancel, - MOpInUnitState::FinishedAndOrCausedCancel => renamed.caused_cancel.is_some(), - }; renamed.is_speculative = false; - if can_cause_cancel { + if renamed.can_cause_cancel() { break; } } @@ -1719,6 +1749,7 @@ async fn rename_execute_retire_run( enqueue, inputs_ready, is_no_longer_speculative, + is_no_longer_speculative_prediction, cant_cause_cancel: _, output_ready: _, finish_cause_cancel: _, @@ -1747,6 +1778,11 @@ async fn rename_execute_retire_run( state.get_unit_mop_is_no_longer_speculative(unit_index), ) .await; + sim.write( + is_no_longer_speculative_prediction, + state.get_unit_mop_is_no_longer_speculative_prediction(unit_index), + ) + .await; sim.write(unit_outputs_ready, !is_canceling).await; } sim.wait_for_clock_edge(cd.clk).await; @@ -1767,6 +1803,7 @@ async fn rename_execute_retire_run( enqueue, inputs_ready, is_no_longer_speculative, + is_no_longer_speculative_prediction: _, // no action necessary cant_cause_cancel, output_ready, finish_cause_cancel, @@ -1959,6 +1996,7 @@ pub fn rename_execute_retire(config: PhantomConst) { enqueue, inputs_ready, is_no_longer_speculative, + is_no_longer_speculative_prediction, cant_cause_cancel: _, output_ready: _, finish_cause_cancel: _, @@ -1992,6 +2030,12 @@ pub fn rename_execute_retire(config: PhantomConst) { (is_no_longer_speculative.ty()).HdlNone(), ) .await; + sim.write( + is_no_longer_speculative_prediction, + #[hdl(sim)] + (is_no_longer_speculative_prediction.ty()).HdlNone(), + ) + .await; sim.write(unit_outputs_ready, false).await; } }, diff --git a/crates/cpu/src/rename_execute_retire/reorder_buffer.rs b/crates/cpu/src/rename_execute_retire/reorder_buffer.rs index 0ec9aa9..e9c2fc6 100644 --- a/crates/cpu/src/rename_execute_retire/reorder_buffer.rs +++ b/crates/cpu/src/rename_execute_retire/reorder_buffer.rs @@ -173,6 +173,21 @@ impl RobEntry { } } } + pub(crate) fn can_cause_cancel(&self) -> bool { + match self.mop_in_unit_state { + MOpInUnitState::NotYetEnqueued => true, + MOpInUnitState::InputsNotReadySpeculative { can_cause_cancel } => can_cause_cancel, + MOpInUnitState::InputsReady { + speculative: _, + can_cause_cancel, + } => can_cause_cancel, + MOpInUnitState::OutputReady { + speculative: _, + can_cause_cancel, + } => can_cause_cancel, + MOpInUnitState::FinishedAndOrCausedCancel => self.caused_cancel.is_some(), + } + } } #[hdl] diff --git a/crates/cpu/src/test/decode_and_run_single_insn.rs b/crates/cpu/src/test/decode_and_run_single_insn.rs index debce11..c4d09ed 100644 --- a/crates/cpu/src/test/decode_and_run_single_insn.rs +++ b/crates/cpu/src/test/decode_and_run_single_insn.rs @@ -204,6 +204,7 @@ fn connect_to_unit_nop>( enqueue, inputs_ready, is_no_longer_speculative, + is_no_longer_speculative_prediction, cant_cause_cancel: _, output_ready: _, finish_cause_cancel: _, @@ -218,6 +219,10 @@ fn connect_to_unit_nop>( is_no_longer_speculative, is_no_longer_speculative.ty().HdlNone(), ); + connect( + is_no_longer_speculative_prediction, + is_no_longer_speculative_prediction.ty().HdlNone(), + ); connect(unit_outputs_ready, false); connect(cancel_all.data, HdlNone()); } @@ -506,6 +511,7 @@ impl DecodeAndRunSingleInsnState DecodeAndRunSingleInsnState( enqueue, inputs_ready, is_no_longer_speculative, + is_no_longer_speculative_prediction, cant_cause_cancel: _, output_ready: _, finish_cause_cancel: _, @@ -2168,6 +2169,11 @@ async fn load_store_tester_impl( state.to_load_store_is_no_longer_speculative(), ) .await; + sim.write( + is_no_longer_speculative_prediction, + state.to_load_store_is_no_longer_speculative_prediction(), + ) + .await; } else { sim.write(enqueue.data, enqueue.ty().data.HdlNone()).await; sim.write(inputs_ready, inputs_ready.ty().HdlNone()).await; @@ -2176,6 +2182,11 @@ async fn load_store_tester_impl( is_no_longer_speculative.ty().HdlNone(), ) .await; + sim.write( + is_no_longer_speculative_prediction, + is_no_longer_speculative_prediction.ty().HdlNone(), + ) + .await; } sim.write(cancel_all.data, state.to_load_store_cancel_all()) .await; @@ -2190,6 +2201,7 @@ async fn load_store_tester_impl( enqueue, inputs_ready, is_no_longer_speculative, + is_no_longer_speculative_prediction, cant_cause_cancel, output_ready, finish_cause_cancel, @@ -2213,6 +2225,15 @@ async fn load_store_tester_impl( { state.handle_to_load_store_is_no_longer_speculative(is_no_longer_speculative); } + #[hdl(sim)] + if let HdlSome(is_no_longer_speculative_prediction) = sim + .read_past(is_no_longer_speculative_prediction, cd.clk) + .await + { + state.handle_to_load_store_is_no_longer_speculative_prediction( + is_no_longer_speculative_prediction, + ); + } if sim.read_past_bool(unit_outputs_ready, cd.clk).await { #[hdl(sim)] if let HdlSome(cant_cause_cancel) = sim.read_past(cant_cause_cancel, cd.clk).await { @@ -2293,6 +2314,7 @@ fn load_store_tester(config: PhantomConst) { enqueue, inputs_ready, is_no_longer_speculative, + is_no_longer_speculative_prediction, cant_cause_cancel: _, output_ready: _, finish_cause_cancel: _, @@ -2318,6 +2340,11 @@ fn load_store_tester(config: PhantomConst) { is_no_longer_speculative.ty().HdlNone(), ) .await; + sim.write( + is_no_longer_speculative_prediction, + is_no_longer_speculative_prediction.ty().HdlNone(), + ) + .await; sim.write(unit_outputs_ready, false).await; sim.write(cancel_all.data, HdlNone()).await; sim.write(test_passed, false).await;