WIP: adding ExecuteToUnitInterface::is_no_longer_speculative_prediction
Some checks failed
/ test (pull_request) Failing after 4m7s
Some checks failed
/ test (pull_request) Failing after 4m7s
This commit is contained in:
parent
a81d85d9c0
commit
6e3486bfbb
6 changed files with 269 additions and 59 deletions
|
|
@ -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 µ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;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue