mod gate;
mod retire;
mod stats;
mod writes;
pub use writes::{committed_writes_pending, send_one_write};
use crate::arch::regs::vpr::Vpr;
use crate::common::PhysAddr;
use crate::isa::op::MemWidth;
use crate::isa::privileged::Trap;
use crate::isa::rvv::VRegIdx;
use crate::uarch::ctx::CoreCtx;
use crate::uarch::pipeline::engine::BackendCommon;
use crate::uarch::pipeline::lsq::load_queue::LoadQueue;
use crate::uarch::pipeline::lsq::store_buffer::StoreBuffer;
use crate::uarch::pipeline::lsq::vec_store_buffer::VecStoreBuffer;
use crate::uarch::pipeline::rename::checkpoint::{CheckpointId, CheckpointTable};
use crate::uarch::pipeline::rename::free_list::FreeList;
use crate::uarch::pipeline::rename::map::RenameMap;
use crate::uarch::pipeline::rename::prf::PhysReg;
use crate::uarch::pipeline::rename::scoreboard::Scoreboard;
use crate::uarch::pipeline::rename::vec_prf::VecPhysReg;
use crate::uarch::pipeline::rename::vec_prf::VecPhysRegFile;
use crate::uarch::pipeline::rob::{Rob, RobEntry, RobTag};
use gate::{gate_head, trap_or_interrupt};
use retire::{RetireTargets, retire_entry};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CommitEvent {
Trap(Trap, u64),
ReExecute(u64, ReExecuteCause),
SquashAfter(u64),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReExecuteCause {
StaleLine,
ChangedPte,
}
enum CommitFlow {
Continue,
Stop(Option<CommitEvent>),
}
#[derive(Debug)]
pub struct CommitResources<'a> {
pub common: &'a mut BackendCommon,
pub rob: &'a mut Rob,
pub store_buffer: &'a mut StoreBuffer,
pub vec_store_buffer: &'a mut VecStoreBuffer,
pub width: usize,
pub registers: CommitRegisters<'a>,
}
#[derive(Debug)]
pub enum CommitRegisters<'a> {
Scoreboard(&'a mut Scoreboard),
Renamed {
rename_map: &'a mut RenameMap,
free_list: &'a mut FreeList<PhysReg>,
load_queue: &'a mut LoadQueue,
checkpoints: &'a mut CheckpointTable,
vec_prf: &'a mut VecPhysRegFile,
vec_free_list: &'a mut FreeList<VecPhysReg>,
},
}
impl CommitRegisters<'_> {
fn retire_scalar(&mut self, entry: &RobEntry, is_fp: bool) {
match self {
Self::Scoreboard(scoreboard) => scoreboard.clear_if_match(entry.rd, is_fp, entry.tag),
Self::Renamed { rename_map, free_list, .. } => {
if entry.old_phys_dst.0 != entry.phys_dst.0 {
free_list.reclaim(entry.old_phys_dst);
}
rename_map.set(entry.rd, is_fp, entry.phys_dst);
}
}
}
fn retire_vec(&mut self, vpr: &mut Vpr, entry: &RobEntry, i: usize, vreg: VRegIdx) {
match self {
Self::Scoreboard(scoreboard) => scoreboard.clear_vec_if_match(vreg, entry.tag),
Self::Renamed { rename_map, vec_prf, vec_free_list, .. } => {
vpr.write_bytes(vreg, vec_prf.read_bytes(entry.vec_phys_dst[i]));
if entry.vec_old_phys_dst[i] != entry.vec_phys_dst[i] {
vec_free_list.reclaim(entry.vec_old_phys_dst[i]);
}
rename_map.set_vec(vreg, entry.vec_phys_dst[i]);
}
}
}
fn reclaim_faulted(&mut self, entry: &RobEntry) {
let Self::Renamed { free_list, vec_free_list, .. } = self else {
return;
};
if entry.phys_dst.0 != 0 {
free_list.reclaim(entry.phys_dst);
}
for i in 0..entry.vec_dst_count as usize {
if !entry.vec_phys_dst[i].is_zero() {
vec_free_list.reclaim(entry.vec_phys_dst[i]);
}
}
}
fn release_load(&mut self, tag: RobTag) {
if let Self::Renamed { load_queue, .. } = self {
load_queue.deallocate(tag);
}
}
fn free_checkpoint(&mut self, id: CheckpointId) {
if let Self::Renamed { checkpoints, .. } = self {
checkpoints.free(id);
}
}
}
pub fn commit_stage(state: &mut CoreCtx<'_>, res: CommitResources<'_>) -> Option<CommitEvent> {
let CommitResources { common, rob, store_buffer, vec_store_buffer, width, mut registers } = res;
let now = state.cycle;
common.deliver_commit_notices(&mut state.core.branch_predictor, now);
if let CommitFlow::Stop(event) = trap_or_interrupt(state, common, rob) {
return event;
}
let mut event = None;
let mut retired_count: usize = 0;
let mut youngest_retired = None;
let rob_empty_at_start = rob.peek_head().is_none();
for _ in 0..width {
let gate = gate_head(state, common, rob, store_buffer, vec_store_buffer, &mut registers);
if let CommitFlow::Stop(stop) = gate {
event = stop;
break;
}
let Some(entry) = rob.commit_head() else { break };
retired_count += 1;
youngest_retired = Some(entry.seq);
let mut targets =
RetireTargets { common, store_buffer, vec_store_buffer, registers: &mut registers };
if let CommitFlow::Stop(stop) = retire_entry(state, &mut targets, &entry) {
event = stop;
break;
}
}
if let Some(seq) = youngest_retired {
common.note_committed(seq, state.cycle);
}
count_retire_width(state, retired_count, rob_empty_at_start);
send_one_write(state, common, store_buffer, vec_store_buffer);
event
}
fn count_retire_width(state: &mut CoreCtx<'_>, retired: usize, rob_empty_at_start: bool) {
let paths = &state.core.stat_paths;
if retired == 0 && rob_empty_at_start {
state.uncore.stats.counter(paths.pipeline.cycles_rob_empty).inc();
}
let bucket = match retired.min(3) {
0 => paths.commit.retire_hist_zero,
1 => paths.commit.retire_hist_one,
2 => paths.commit.retire_hist_two,
_ => paths.commit.retire_hist_three_plus,
};
state.uncore.stats.counter(bucket).inc();
}
fn is_pure_ram(state: &CoreCtx<'_>, paddr: PhysAddr, width: MemWidth) -> bool {
state.bus.is_ram(paddr, width.bytes())
}
#[cfg(test)]
#[allow(clippy::unwrap_used, unused_results)]
mod tests {
use super::*;
use crate::config::Config;
use crate::isa::instruction::InstSize;
use crate::isa::reg::RegIdx;
#[test]
fn test_commit_stage_normal() {
let config = Config::default();
let mut sys = crate::system::SystemState::build(&config, "");
let mut state = sys.core_ctx(0);
let mut rob = Rob::new(4);
let mut store_buffer = StoreBuffer::new(4);
let mut vec_store_buffer = VecStoreBuffer::new(4, crate::config::VecStoreForwarding::Off);
let mut scoreboard = Scoreboard::new();
let ctrl = crate::exec::signals::ControlSignals { reg_write: true, ..Default::default() };
let tag = rob
.allocate(
0x1000,
0,
InstSize::Standard,
RegIdx::new(1),
ctrl,
crate::uarch::pipeline::rename::prf::PhysReg(1),
crate::uarch::pipeline::rename::prf::PhysReg(0),
crate::common::InstSeq::default(),
)
.unwrap();
rob.complete(tag, 42);
let mut common = BackendCommon::default();
let trap = commit_stage(
&mut state,
CommitResources {
common: &mut common,
rob: &mut rob,
store_buffer: &mut store_buffer,
vec_store_buffer: &mut vec_store_buffer,
width: 1,
registers: CommitRegisters::Scoreboard(&mut scoreboard),
},
);
assert!(trap.is_none());
assert_eq!(state.hart.regs.read(RegIdx::new(1)), 42);
}
}