WIP: adding ExecuteToUnitInterface::is_no_longer_speculative_prediction
Some checks failed
/ test (pull_request) Failing after 4m7s

This commit is contained in:
Jacob Lifshay 2026-07-31 16:51:04 -07:00
parent a81d85d9c0
commit 6e3486bfbb
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
6 changed files with 269 additions and 59 deletions

View file

@ -203,6 +203,18 @@ pub struct UnitMOpCantCauseCancel<C: PhantomConstGet<CpuConfig>> {
pub config: C,
}
/// This tells the Unit that:
/// * Other Units can't cause any cancels for any µOps before [`Self::id`] (so all µOps before then are either assigned to this Unit or have reported that they can't cause a cancel or have finished without causing a cancel).
///
/// The rename/execute/retire logic guarantees:
/// * [`Self::id`] refers to a µOp that is/was enqueued in this Unit.
/// * This was not previously told to the unit.
#[hdl(no_static)]
pub struct UnitMOpOtherUnitsCantCauseCancelsBefore<C: PhantomConstGet<CpuConfig>> {
pub id: MOpId,
pub config: C,
}
/// Interface from the Rename/Execute/Retire control logic to a single Unit.
///
/// ## State diagram for a single &micro;Op in a Unit
@ -223,6 +235,9 @@ pub struct ExecuteToUnitInterface<C: PhantomConstGet<CpuConfig>> {
pub inputs_ready: HdlOption<UnitInputsReady<C>>,
/// if [`Self::unit_outputs_ready`] is `false`, then this is always [`HdlNone`]
pub is_no_longer_speculative: HdlOption<UnitMOpIsNoLongerSpeculative<C>>,
/// if [`Self::unit_outputs_ready`] is `false`, then this is always [`HdlNone`]
pub other_units_cant_cause_cancels_before:
HdlOption<UnitMOpOtherUnitsCantCauseCancelsBefore<C>>,
/// this uses [`Self::unit_outputs_ready`] as a shared ready flag
#[hdl(flip)]
pub cant_cause_cancel: HdlOption<UnitMOpCantCauseCancel<C>>,
@ -466,6 +481,16 @@ impl MOpInUnitState {
Self::FinishedAndOrCausedCancel => true,
}
}
/// return `true` if this µOp has been enqueued and has neither finished nor caused a cancel.
fn is_active(self) -> bool {
match self {
Self::NotYetEnqueued => false,
Self::InputsNotReadySpeculative { .. }
| Self::InputsReady { .. }
| Self::OutputReady { .. } => true,
Self::FinishedAndOrCausedCancel => false,
}
}
}
impl fmt::Debug for MOpInUnitState {
@ -614,6 +639,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
all_prior_mops_finished_and_or_caused_cancel,
output,
caused_cancel,
other_units_cant_cause_cancels_before,
sent_other_units_cant_cause_cancels_before,
} in self.rob.renamed()
{
let masked_id = mop.id.as_int() as usize & mask;
@ -631,6 +658,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
if *all_prior_mops_finished_and_or_caused_cancel {
f.write_str("(apfc)")?;
}
if *sent_other_units_cant_cause_cancels_before {
f.write_str("(socccb)")?;
} else if *other_units_cant_cause_cancels_before {
f.write_str("(occcb)")?;
}
if output.is_some() {
f.write_str("(output)")?;
}
@ -647,7 +679,6 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
)
})
.to_string();
// TODO
}
retval
}
@ -1219,6 +1250,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
all_prior_mops_finished_and_or_caused_cancel: _,
output: _,
caused_cancel: _,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} in self.rob.renamed()
{
if *done_waiting_for_register_fences
@ -1263,31 +1296,50 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
fn get_unit_mop_is_no_longer_speculative(
&self,
unit_index: usize,
) -> SimValue<HdlOption<UnitMOpIsNoLongerSpeculative<C>>> {
let ret_ty = HdlOption[UnitMOpIsNoLongerSpeculative[self.config]];
) -> Option<SimValue<UnitMOpIsNoLongerSpeculative<C>>> {
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_other_units_cant_cause_cancels_before(
&self,
unit_index: usize,
) -> Option<SimValue<UnitMOpOtherUnitsCantCauseCancelsBefore<C>>> {
if self.is_canceling() {
return None;
}
for rob in self.rob.renamed() {
if rob.unit_index == unit_index
&& rob.other_units_cant_cause_cancels_before
&& rob.is_speculative
&& !rob.sent_other_units_cant_cause_cancels_before
{
return Some(
#[hdl(sim)]
UnitMOpOtherUnitsCantCauseCancelsBefore::<_> {
id: &rob.mop.id,
config: self.config,
},
);
}
}
None
}
#[hdl]
fn unit_output_ready(&mut self, output_ready: SimValue<UnitOutputReady<C>>) {
@ -1310,6 +1362,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
all_prior_mops_finished_and_or_caused_cancel: _,
output,
caused_cancel,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = rob;
assert!(output.is_none());
assert!(caused_cancel.is_none());
@ -1345,6 +1399,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
all_prior_mops_finished_and_or_caused_cancel: _,
output,
caused_cancel,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = self.rob.renamed_by_id_mut(&id);
assert!(caused_cancel.is_none());
let cancel_after_retire;
@ -1459,6 +1515,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
all_prior_mops_finished_and_or_caused_cancel: _,
output,
caused_cancel,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = renamed_entry
{
if caused_cancel.is_some() {
@ -1584,6 +1642,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
all_prior_mops_finished_and_or_caused_cancel: _,
output: _,
caused_cancel,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} in renamed_entries
{
assert_eq!(mop_in_unit_state, MOpInUnitState::FinishedAndOrCausedCancel);
@ -1618,24 +1678,24 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
};
}
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;
}
}
let mut unit_that_can_cause_cancel = None;
for renamed in self.rob.renamed_mut() {
if unit_that_can_cause_cancel.is_none_or(|v| v == renamed.unit_index) {
renamed.other_units_cant_cause_cancels_before = true;
}
if renamed.can_cause_cancel() {
let unit_that_can_cause_cancel =
*unit_that_can_cause_cancel.get_or_insert(renamed.unit_index);
if unit_that_can_cause_cancel != renamed.unit_index {
break;
}
}
}
for renamed in self.rob.renamed_mut() {
let RobEntry {
is_register_fence,
@ -1647,6 +1707,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
all_prior_mops_finished_and_or_caused_cancel,
output: _,
caused_cancel: _,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = &renamed;
if *is_register_fence {
if !*all_prior_mops_finished_and_or_caused_cancel || *is_speculative {
@ -1671,6 +1733,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
all_prior_mops_finished_and_or_caused_cancel: _,
output: _,
caused_cancel: Some(caused_cancel),
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
}) = first_renamed
&& !*caused_cancel.cancel_after_retire
{
@ -1719,6 +1783,7 @@ async fn rename_execute_retire_run(
enqueue,
inputs_ready,
is_no_longer_speculative,
other_units_cant_cause_cancels_before,
cant_cause_cancel: _,
output_ready: _,
finish_cause_cancel: _,
@ -1747,6 +1812,11 @@ async fn rename_execute_retire_run(
state.get_unit_mop_is_no_longer_speculative(unit_index),
)
.await;
sim.write(
other_units_cant_cause_cancels_before,
state.get_unit_mop_other_units_cant_cause_cancels_before(unit_index),
)
.await;
sim.write(unit_outputs_ready, !is_canceling).await;
}
sim.wait_for_clock_edge(cd.clk).await;
@ -1767,6 +1837,7 @@ async fn rename_execute_retire_run(
enqueue,
inputs_ready,
is_no_longer_speculative,
other_units_cant_cause_cancels_before,
cant_cause_cancel,
output_ready,
finish_cause_cancel,
@ -1788,6 +1859,8 @@ async fn rename_execute_retire_run(
all_prior_mops_finished_and_or_caused_cancel: _,
output,
caused_cancel,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = state.rob.renamed_by_id_mut(&enqueue.mop.id);
assert!(output.is_none());
assert!(caused_cancel.is_none());
@ -1809,6 +1882,8 @@ async fn rename_execute_retire_run(
all_prior_mops_finished_and_or_caused_cancel: _,
output,
caused_cancel,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = state.rob.renamed_by_id_mut(&inputs_ready.mop.id);
assert!(*done_waiting_for_register_fences);
assert!(output.is_none());
@ -1832,11 +1907,37 @@ async fn rename_execute_retire_run(
all_prior_mops_finished_and_or_caused_cancel: _,
output: _,
caused_cancel: _,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = state.rob.renamed_by_id_mut(&is_no_longer_speculative.id);
*mop_in_unit_state = mop_in_unit_state
.without_speculative()
.expect("UnitMOpIsNoLongerSpeculative is known to be valid");
}
#[hdl(sim)]
if let HdlSome(other_units_cant_cause_cancels_before) = sim
.read_past(other_units_cant_cause_cancels_before, cd.clk)
.await
{
assert!(!state.is_canceling());
let RobEntry {
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: _,
other_units_cant_cause_cancels_before,
sent_other_units_cant_cause_cancels_before,
} = state
.rob
.renamed_by_id_mut(&other_units_cant_cause_cancels_before.id);
assert!(*other_units_cant_cause_cancels_before);
*sent_other_units_cant_cause_cancels_before = true;
}
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 {
@ -1853,6 +1954,8 @@ async fn rename_execute_retire_run(
all_prior_mops_finished_and_or_caused_cancel: _,
output: _,
caused_cancel,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = state.rob.renamed_by_id_mut(&id);
assert!(caused_cancel.is_none());
*mop_in_unit_state = mop_in_unit_state
@ -1959,6 +2062,7 @@ pub fn rename_execute_retire(config: PhantomConst<CpuConfig>) {
enqueue,
inputs_ready,
is_no_longer_speculative,
other_units_cant_cause_cancels_before,
cant_cause_cancel: _,
output_ready: _,
finish_cause_cancel: _,
@ -1992,6 +2096,12 @@ pub fn rename_execute_retire(config: PhantomConst<CpuConfig>) {
(is_no_longer_speculative.ty()).HdlNone(),
)
.await;
sim.write(
other_units_cant_cause_cancels_before,
#[hdl(sim)]
(other_units_cant_cause_cancels_before.ty()).HdlNone(),
)
.await;
sim.write(unit_outputs_ready, false).await;
}
},

View file

@ -40,6 +40,8 @@ struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
all_prior_mops_finished_and_or_caused_cancel: Bool,
output: HdlOption<NextPcPredictorOp<C>>,
caused_cancel: HdlOption<UnitCausedCancel<C>>,
other_units_cant_cause_cancels_before: Bool,
sent_other_units_cant_cause_cancels_before: Bool,
}
impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
@ -55,6 +57,8 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
all_prior_mops_finished_and_or_caused_cancel: _,
output,
caused_cancel,
other_units_cant_cause_cancels_before: _,
sent_other_units_cant_cause_cancels_before: _,
} = self;
#[hdl(sim)]
Self {
@ -69,6 +73,8 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
output.HdlNone(),
caused_cancel: #[hdl(sim)]
caused_cancel.HdlNone(),
other_units_cant_cause_cancels_before: false,
sent_other_units_cant_cause_cancels_before: false,
}
}
}
@ -87,6 +93,8 @@ pub(crate) struct RobEntry<C: PhantomConstCpuConfig> {
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>>>,
pub(crate) other_units_cant_cause_cancels_before: bool,
pub(crate) sent_other_units_cant_cause_cancels_before: bool,
}
impl<C: PhantomConstCpuConfig> RobEntry<C> {
@ -105,6 +113,8 @@ impl<C: PhantomConstCpuConfig> RobEntry<C> {
all_prior_mops_finished_and_or_caused_cancel: false,
output: None,
caused_cancel: None,
other_units_cant_cause_cancels_before: false,
sent_other_units_cant_cause_cancels_before: false,
}
}
fn dest_reg(&self) -> Option<&SimValue<PRegNum<C>>> {
@ -131,6 +141,8 @@ impl<C: PhantomConstCpuConfig> RobEntry<C> {
all_prior_mops_finished_and_or_caused_cancel,
output,
caused_cancel,
other_units_cant_cause_cancels_before,
sent_other_units_cant_cause_cancels_before,
} = self;
let ret_ty = RobEntryDebugState[config];
#[hdl(sim)]
@ -144,6 +156,8 @@ impl<C: PhantomConstCpuConfig> RobEntry<C> {
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),
other_units_cant_cause_cancels_before,
sent_other_units_cant_cause_cancels_before,
}
}
#[hdl]
@ -173,6 +187,21 @@ impl<C: PhantomConstCpuConfig> RobEntry<C> {
}
}
}
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]

View file

@ -204,6 +204,7 @@ fn connect_to_unit_nop<C: Type + PhantomConstGet<CpuConfig>>(
enqueue,
inputs_ready,
is_no_longer_speculative,
other_units_cant_cause_cancels_before,
cant_cause_cancel: _,
output_ready: _,
finish_cause_cancel: _,
@ -218,6 +219,10 @@ fn connect_to_unit_nop<C: Type + PhantomConstGet<CpuConfig>>(
is_no_longer_speculative,
is_no_longer_speculative.ty().HdlNone(),
);
connect(
other_units_cant_cause_cancels_before,
other_units_cant_cause_cancels_before.ty().HdlNone(),
);
connect(unit_outputs_ready, false);
connect(cancel_all.data, HdlNone());
}
@ -506,6 +511,7 @@ impl<C: PhantomConstCpuConfig, MaxMOpCount: Size> DecodeAndRunSingleInsnState<C,
enqueue,
inputs_ready,
is_no_longer_speculative,
other_units_cant_cause_cancels_before,
cant_cause_cancel: _, // we don't track if it can cause a cancel
output_ready,
finish_cause_cancel,
@ -513,6 +519,10 @@ impl<C: PhantomConstCpuConfig, MaxMOpCount: Size> DecodeAndRunSingleInsnState<C,
cancel_all,
config: _,
} = to_unit;
connect(
other_units_cant_cause_cancels_before,
other_units_cant_cause_cancels_before.ty().HdlNone(),
);
connect(unit_outputs_ready, true);
connect(cancel_all.data, HdlNone());
#[hdl]

View file

@ -1260,6 +1260,7 @@ pub fn alu_branch(
enqueue,
inputs_ready,
is_no_longer_speculative: _, // we don't care about being speculative for these instructions
other_units_cant_cause_cancels_before: _, // we don't care about other instructions being able to cause cancels
cant_cause_cancel,
output_ready,
finish_cause_cancel,

View file

@ -13,7 +13,7 @@ use crate::{
rename_execute_retire::{
ExecuteToUnitInterface, GlobalState, MOpId, MOpInstance, NextPcPredictorOp, UnitEnqueue,
UnitFinishCauseCancel, UnitInputsReady, UnitMOpCantCauseCancel,
UnitMOpIsNoLongerSpeculative, UnitOutputReady,
UnitMOpIsNoLongerSpeculative, UnitMOpOtherUnitsCantCauseCancelsBefore, UnitOutputReady,
},
unit::{DynUnit, DynUnitWrapper, RenamedMOpFilter, UnitIO, UnitKind, UnitMOp, UnitTrait},
};
@ -53,6 +53,8 @@ pub struct DCacheStart<C: PhantomConstGet<CpuConfig>> {
}
#[hdl]
/// All errors here cause asynchronous error interrupts, so anything requiring precise exceptions (e.g. page
/// faults) must happen before the D-cache.
pub enum DCacheFinishStatus {
/// The operation finished successfully
Success,
@ -116,7 +118,7 @@ pub struct LoadStoreToDCacheInterface<C: PhantomConstGet<CpuConfig>> {
/// Operations started through `start` must maintain sequential consistency -- so earlier operations
/// can't appear to have happened after later operations.
pub start: ReadyValid<DCacheStart<C>>,
/// operations must finish in the same order they started.
/// Operations must finish in the same order they started.
#[hdl(flip)]
pub finish: ReadyValid<DCacheFinish<C>>,
/// It's valid for `start_speculative_load.ready` to be `false` at any time (or even always)
@ -200,9 +202,7 @@ struct Op<C: PhantomConstCpuConfig> {
pc: SimValue<UInt<64>>,
size_in_bytes: u8,
mop: SimValue<LoadStoreMOp<PRegNum<C>, PRegNum<C>>>,
/// earlier instructions can cause a cancel, so this instruction is still speculative.
/// not to be confused with this instruction being able to cause a cancel.
is_speculative: bool,
other_units_cant_cause_cancels_before: bool,
src_values: Option<[SimValue<TraceAsString<PRegValue>>; COMMON_MOP_SRC_LEN]>,
dest_value: Option<SimValue<TraceAsString<PRegValue>>>,
sent_cant_cause_cancel: bool,
@ -218,15 +218,15 @@ impl<C: PhantomConstCpuConfig> fmt::Display for Op<C> {
pc,
size_in_bytes,
mop,
is_speculative,
other_units_cant_cause_cancels_before,
src_values,
dest_value,
sent_cant_cause_cancel,
sent_output_ready,
state,
} = self;
if *is_speculative {
f.write_str("(s)")?;
if *other_units_cant_cause_cancels_before {
f.write_str("(occcb)")?;
}
if *sent_cant_cause_cancel {
f.write_str("(sccc)")?;
@ -262,7 +262,7 @@ impl<C: PhantomConstCpuConfig> Op<C> {
pc,
size_in_bytes,
mop,
is_speculative: true,
other_units_cant_cause_cancels_before: false,
src_values: None,
dest_value: None,
sent_cant_cause_cancel: false,
@ -300,16 +300,12 @@ impl<C: PhantomConstCpuConfig> Op<C> {
}
}
fn can_cause_cancel(&self) -> bool {
if self.is_speculative {
return true;
}
match self.state {
OpState::InputNotReady
| OpState::StartedSpeculativeLoad
| OpState::FinishedSpeculativeLoad
| OpState::StartedLoadStore => true,
OpState::FinishedSpeculativeLoad | OpState::FinishedLoadStore => {
self.dest_value.is_none()
}
OpState::FinishedLoadStore => self.dest_value.is_none(),
}
}
}
@ -384,9 +380,12 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
filter,
}
}
fn try_op_by_id(&mut self, id: &<MOpId as Type>::SimValue) -> Option<&mut Op<C>> {
self.ops.iter_mut().find(|op| *op.id == *id)
}
#[track_caller]
fn op_by_id(&mut self, id: &<MOpId as Type>::SimValue) -> &mut Op<C> {
match self.ops.iter_mut().find(|op| *op.id == *id) {
match self.try_op_by_id(id) {
Some(op) => op,
None => panic!("can't find load_store Op with id {id:?}"),
}
@ -439,7 +438,17 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
) {
#[hdl(sim)]
let UnitMOpIsNoLongerSpeculative::<_> { id, config: _ } = is_no_longer_speculative;
self.op_by_id(&id).is_speculative = false;
self.op_by_id(&id).other_units_cant_cause_cancels_before = true;
}
#[hdl]
fn from_execute_other_units_cant_cause_cancels_before(
&mut self,
other_units_cant_cause_cancels_before: SimValue<UnitMOpOtherUnitsCantCauseCancelsBefore<C>>,
) {
#[hdl(sim)]
let UnitMOpOtherUnitsCantCauseCancelsBefore::<_> { id, config: _ } =
other_units_cant_cause_cancels_before;
self.op_by_id(&id).other_units_cant_cause_cancels_before = true;
}
#[hdl]
fn from_execute_cant_cause_cancel(&self) -> Option<SimValue<UnitMOpCantCauseCancel<C>>> {
@ -518,14 +527,14 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
pc: _,
size_in_bytes: _,
mop: _,
is_speculative,
other_units_cant_cause_cancels_before,
src_values: _,
dest_value,
sent_cant_cause_cancel: _,
sent_output_ready,
state,
} = self.ops.front()?;
if *is_speculative {
if !*other_units_cant_cause_cancels_before {
return None;
}
let retval_ty = UnitFinishCauseCancel[self.config];
@ -809,25 +818,35 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
}
#[hdl]
fn step(&mut self) {
if self.canceling {
let Self {
global_state: _,
ops,
d_cache_start,
d_cache_start_speculative_load,
canceling,
config,
unit_index: _,
filter: _,
} = self;
if *canceling {
return self.canceling_step();
}
self.d_cache_start = None;
self.d_cache_start_speculative_load = None;
*d_cache_start = None;
*d_cache_start_speculative_load = None;
let store_queue_capacity = 4; // TODO: make configurable
struct StoreQueueEntry {
access_range: AddressRange,
data: [u8; MAX_LOAD_STORE_SIZE],
}
let mut store_queue = Vec::<StoreQueueEntry>::with_capacity(store_queue_capacity);
for op in &mut self.ops {
for op in ops {
let access_range = op.access_range();
let Op {
ref id,
pc: _,
size_in_bytes: _,
ref mop,
is_speculative,
other_units_cant_cause_cancels_before,
ref src_values,
ref mut dest_value,
sent_cant_cause_cancel: _,
@ -871,21 +890,23 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
let mask = std::array::from_fn(|i| i < size.get() as usize);
match state {
OpState::InputNotReady
if is_speculative && !blocking_store && is_cacheable =>
if !other_units_cant_cause_cancels_before
&& !blocking_store
&& is_cacheable =>
{
self.d_cache_start_speculative_load.get_or_insert_with(|| {
d_cache_start_speculative_load.get_or_insert_with(|| {
#[hdl(sim)]
DCacheStartSpeculativeLoad::<_> {
id,
address,
mask,
config: self.config,
config,
}
});
}
OpState::InputNotReady | OpState::FinishedSpeculativeLoad => {
if !is_speculative && dest_value.is_none() {
self.d_cache_start.get_or_insert_with(|| {
if other_units_cant_cause_cancels_before && dest_value.is_none() {
d_cache_start.get_or_insert_with(|| {
#[hdl(sim)]
DCacheStart::<_> {
id,
@ -896,7 +917,7 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
),
address,
data_and_mask: mask.map(|v| v.then_some(0)),
config: self.config,
config,
}
});
}
@ -912,7 +933,7 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
pc: _,
size_in_bytes: _,
mop: _,
is_speculative,
other_units_cant_cause_cancels_before,
ref src_values,
ref dest_value,
sent_cant_cause_cancel: _,
@ -939,8 +960,8 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
}
match state {
OpState::InputNotReady => {
if !is_speculative {
self.d_cache_start.get_or_insert_with(|| {
if other_units_cant_cause_cancels_before {
d_cache_start.get_or_insert_with(|| {
#[hdl(sim)]
DCacheStart::<_> {
id,
@ -951,7 +972,7 @@ impl<C: PhantomConstCpuConfig> LoadStoreState<C> {
),
address,
data_and_mask,
config: self.config,
config,
}
});
}
@ -988,6 +1009,7 @@ async fn load_store_impl(
enqueue,
inputs_ready: _,
is_no_longer_speculative: _,
other_units_cant_cause_cancels_before: _,
cant_cause_cancel,
output_ready,
finish_cause_cancel,
@ -1042,6 +1064,7 @@ async fn load_store_impl(
enqueue,
inputs_ready,
is_no_longer_speculative,
other_units_cant_cause_cancels_before,
cant_cause_cancel,
output_ready,
finish_cause_cancel,
@ -1066,6 +1089,15 @@ async fn load_store_impl(
{
state.from_execute_is_no_longer_speculative(is_no_longer_speculative);
}
#[hdl(sim)]
if let HdlSome(other_units_cant_cause_cancels_before) = sim
.read_past(other_units_cant_cause_cancels_before, cd.clk)
.await
{
state.from_execute_other_units_cant_cause_cancels_before(
other_units_cant_cause_cancels_before,
);
}
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 {
@ -1221,6 +1253,7 @@ pub fn load_store(
enqueue,
inputs_ready: _,
is_no_longer_speculative: _,
other_units_cant_cause_cancels_before: _,
cant_cause_cancel,
output_ready,
finish_cause_cancel,

View file

@ -2149,6 +2149,7 @@ async fn load_store_tester_impl<C: PhantomConstCpuConfig>(
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<C: PhantomConstCpuConfig>(
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<C: PhantomConstCpuConfig>(
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<C: PhantomConstCpuConfig>(
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<C: PhantomConstCpuConfig>(
{
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<CpuConfig>) {
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<CpuConfig>) {
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;