use crate::arch::reservation::LrScRecord;
use crate::arch::translation::PteUpdate;
use crate::common::PhysAddr;
use crate::exec::retire;
use crate::isa::fence::Fence;
use crate::isa::op::{MemWidth, SystemOp};
use crate::isa::privileged::Trap;
use crate::trace_trap;
use crate::uarch::ctx::CoreCtx;
use crate::uarch::pipeline::engine::{BackendCommon, PendingTrap, TrapProgress};
use crate::uarch::pipeline::lsq::store_buffer::StoreBuffer;
use crate::uarch::pipeline::lsq::vec_store_buffer::VecStoreBuffer;
use crate::uarch::pipeline::lsq::write_buffer::WriteCombiningBuffer;
use crate::uarch::pipeline::rob::{Rob, RobEntry, RobState};
use super::{CommitEvent, CommitFlow, CommitRegisters, ReExecuteCause};
pub(super) fn trap_or_interrupt(
state: &mut CoreCtx<'_>,
common: &mut BackendCommon,
rob: &Rob,
) -> CommitFlow {
if let TrapProgress::Pending(pending) = &common.trap {
state.uncore.stats.counter(state.core.stat_paths.commit.retire_hist_zero).inc();
if state.cycle < pending.taken_at {
return CommitFlow::Stop(None);
}
let pending = pending.clone();
common.trap = TrapProgress::None;
return CommitFlow::Stop(take_pending_trap(state, pending));
}
let epc = rob.peek_head().map_or(state.hart.pc, |head| head.pc);
let interrupt =
retire::pending_interrupt(state.hart).filter(|_| !device_access_in_flight(common, rob));
if let Some(interrupt_trap) = interrupt {
common.trap = TrapProgress::DrainingForInterrupt;
let drained = state.hart.wfi_waiting || (rob.is_empty() && common.frontend_empty);
if drained {
trace_trap!(state.trace_trap_enabled(&interrupt_trap);
event = "interrupt",
epc = %crate::common::trace::Hex(epc),
cause = ?interrupt_trap,
mip = %crate::common::trace::Hex(state.hart.csrs.mip),
mie = %crate::common::trace::Hex(state.hart.csrs.mie),
mstatus = %crate::common::trace::Hex(state.hart.csrs.mstatus),
priv_mode = ?state.hart.privilege,
"CM: interrupt detected — pipeline drained"
);
state.uncore.stats.counter(state.core.stat_paths.commit.retire_hist_zero).inc();
return CommitFlow::Stop(schedule_trap(state, common, interrupt_trap, epc));
}
return CommitFlow::Continue;
}
common.trap = TrapProgress::None;
if !state.hart.wfi_waiting {
return CommitFlow::Continue;
}
state.uncore.stats.counter(state.core.stat_paths.commit.retire_hist_zero).inc();
if state.hart.csrs.mip & state.hart.csrs.mie != 0 {
state.hart.wfi_waiting = false;
return CommitFlow::Stop(Some(CommitEvent::SquashAfter(state.hart.pc)));
}
state.uncore.stats.counter(state.core.stat_paths.pipeline.cycles_wfi).inc();
CommitFlow::Stop(None)
}
pub(super) fn gate_head(
state: &mut CoreCtx<'_>,
common: &mut BackendCommon,
rob: &mut Rob,
store_buffer: &StoreBuffer,
vec_store_buffer: &VecStoreBuffer,
registers: &mut CommitRegisters<'_>,
) -> CommitFlow {
let Some(head) = rob.peek_head() else { return CommitFlow::Stop(None) };
if common.will_squash(head.tag) {
return CommitFlow::Stop(None);
}
if head.state == RobState::Completed
&& head.ctrl.mem_read
&& store_buffer.has_unresolved_store_before(head.tag)
{
return CommitFlow::Stop(None);
}
if head.state == RobState::Issued {
return CommitFlow::Stop(None);
}
if head.state == RobState::Completed
&& head.ctrl.splits_store()
&& !store_buffer.has_data(head.tag)
{
return CommitFlow::Stop(None);
}
if head.state == RobState::Faulted {
return CommitFlow::Stop(take_fault(state, common, rob, registers));
}
if head.state == RobState::Completed && lr_read_a_stale_line(state, head) {
state.uncore.stats.counter(state.core.stat_paths.lsq.coherence_replays).inc();
trace_trap!(state.config.general.trace_instructions;
event = "coherence-reexecute",
pc = %crate::common::trace::Hex(head.pc),
rob_tag = head.tag.0,
"CM: LR read a line another hart has since written — re-executing"
);
return CommitFlow::Stop(Some(CommitEvent::ReExecute(head.pc, ReExecuteCause::StaleLine)));
}
if head.state == RobState::Completed
&& head.dirty_updates.iter().any(|update| updated_pte(state, update).is_none())
{
trace_trap!(state.config.general.trace_instructions;
event = "pte-changed-reexecute",
pc = %crate::common::trace::Hex(head.pc),
rob_tag = head.tag.0,
"CM: store's PTE changed since its walk — re-executing"
);
return CommitFlow::Stop(Some(CommitEvent::ReExecute(head.pc, ReExecuteCause::ChangedPte)));
}
if waits_for_older_stores(head)
&& older_stores_pending(store_buffer, vec_store_buffer, &state.core.wcb)
{
return CommitFlow::Stop(None);
}
CommitFlow::Continue
}
fn take_fault(
state: &mut CoreCtx<'_>,
common: &mut BackendCommon,
rob: &mut Rob,
registers: &mut CommitRegisters<'_>,
) -> Option<CommitEvent> {
let entry = rob.commit_head()?;
let the_trap = entry.trap.as_ref()?;
#[cfg(feature = "commit-log")]
if let Some(ref mut log) = state.commit_log {
use std::io::Write;
let skip = matches!(
the_trap,
Trap::InstructionPageFault(_)
| Trap::InstructionAccessFault(_)
| Trap::InstructionAddressMisaligned(_)
);
if !skip {
let _ = writeln!(log, "core 0: 0x{:016x} (0x{:08x})", entry.pc, entry.inst);
}
}
trace_trap!(state.trace_trap_enabled(the_trap);
event = "sync-exception",
pc = %crate::common::trace::Hex(entry.pc),
rob_tag = entry.tag.0,
cause = ?the_trap,
priv_mode = ?state.hart.privilege,
mstatus = %crate::common::trace::Hex(state.hart.csrs.mstatus),
"CM: synchronous exception at commit"
);
if let Some(writes) = &entry.vec_writes {
writes.apply(state.hart.regs.vpr_mut());
}
if let Some(vstart) = entry.fault_vstart {
state.hart.csrs.vstart = vstart;
}
registers.reclaim_faulted(&entry);
schedule_trap(state, common, the_trap.clone(), entry.pc)
}
fn lr_read_a_stale_line(state: &CoreCtx<'_>, head: &RobEntry) -> bool {
let Some(log) = state.memory.write_log() else { return false };
let (Some(observed), Some(LrScRecord::Lr { paddr })) = (head.observed, head.lr_sc) else {
return false;
};
log.written_by_other_since(paddr, state.hart.hart_id, observed)
}
pub(super) fn updated_pte(state: &CoreCtx<'_>, update: &PteUpdate) -> Option<u64> {
read_ram_word(state, update.pte_addr, MemWidth::Double)
.and_then(|current| update.applied_to(current))
}
fn read_ram_word(state: &CoreCtx<'_>, paddr: PhysAddr, width: MemWidth) -> Option<u64> {
if width == MemWidth::Nop || !state.bus.is_ram(paddr, width.bytes()) {
return None;
}
state.memory.read(paddr, width.bytes() as usize)
}
fn schedule_trap(
state: &CoreCtx<'_>,
common: &mut BackendCommon,
trap: Trap,
epc: u64,
) -> Option<CommitEvent> {
let latency = state.config.pipeline.trap_latency;
if latency == 0 {
return Some(CommitEvent::Trap(trap, epc));
}
common.trap = TrapProgress::Pending(PendingTrap { trap, epc, taken_at: state.cycle + latency });
None
}
fn take_pending_trap(state: &mut CoreCtx<'_>, pending: PendingTrap) -> Option<CommitEvent> {
let (is_interrupt, _) = pending.trap.cause();
let trap = if is_interrupt { retire::pending_interrupt(state.hart)? } else { pending.trap };
if is_interrupt {
state.hart.wfi_waiting = false;
}
Some(CommitEvent::Trap(trap, pending.epc))
}
fn device_access_in_flight(common: &BackendCommon, rob: &Rob) -> bool {
rob.peek_head().is_some_and(|head| {
common.outstanding_loads.values().any(|l| l.side_effecting && l.entry.rob_tag == head.tag)
})
}
fn waits_for_older_stores(head: &RobEntry) -> bool {
matches!(head.ctrl.system_op, SystemOp::SfenceVma | SystemOp::FenceI)
|| (head.ctrl.system_op == SystemOp::Fence && Fence::decode(head.inst).pred.w)
}
pub(super) fn older_stores_pending(
store_buffer: &StoreBuffer,
vec_store_buffer: &VecStoreBuffer,
wcb: &WriteCombiningBuffer,
) -> bool {
store_buffer.has_committed_stores()
|| vec_store_buffer.has_committed_stores()
|| wcb.has_pending()
}