use crate::exec::signals::ControlFlow;
use crate::trace_trap;
use crate::trace_writeback;
use crate::uarch::ctx::StageCtx;
use crate::uarch::pipeline::exception::ExceptionStage;
use crate::uarch::pipeline::latches::Mem2WbEntry;
use crate::uarch::pipeline::rob::Rob;
pub fn writeback_stage(state: &StageCtx<'_>, input: &mut Vec<Mem2WbEntry>, rob: &mut Rob) {
let entries = std::mem::take(input);
for wb in entries {
if let Some(ref trap) = wb.trap {
if wb.fp_flags != 0 {
rob.set_fp_flags(wb.rob_tag, wb.fp_flags);
}
rob.fault(
wb.rob_tag,
trap.clone(),
wb.exception_stage.unwrap_or(ExceptionStage::Memory),
);
trace_trap!(state.trace_trap_enabled(trap);
event = "writeback-fault",
pc = %crate::common::trace::Hex(wb.pc),
rob_tag = wb.rob_tag.0,
trap = ?trap,
stage = ?wb.exception_stage,
"WB: entry marked faulted in ROB"
);
continue;
}
let val = if wb.ctrl.mem_read || wb.ctrl.atomic_op.is_some() {
wb.load_data
} else if wb.ctrl.control_flow == ControlFlow::Jump {
wb.pc.wrapping_add(wb.inst_size.as_u64())
} else {
wb.alu
};
if wb.fp_flags != 0 {
rob.set_fp_flags(wb.rob_tag, wb.fp_flags);
}
if !wb.dirty_updates.is_empty() {
rob.set_dirty_updates(wb.rob_tag, wb.dirty_updates);
}
if let Some(sfence_info) = wb.sfence_vma {
rob.set_sfence_vma(wb.rob_tag, sfence_info);
}
if let Some(lr_sc_rec) = wb.lr_sc {
rob.set_lr_sc(wb.rob_tag, lr_sc_rec);
}
if let Some(seq) = wb.observed {
rob.set_observed(wb.rob_tag, seq);
}
rob.complete(wb.rob_tag, val);
trace_writeback!(state.config.general.trace_instructions;
rob_tag = wb.rob_tag.0,
pc = %crate::common::trace::Hex(wb.pc),
result = %crate::common::trace::Hex(val),
from = if wb.ctrl.mem_read { "load" } else if wb.ctrl.control_flow == ControlFlow::Jump { "jump_link" } else { "alu" },
rd = wb.rd.as_usize(),
rd_phys = wb.rd_phys.0,
fp_flags = wb.fp_flags,
"WB: ROB entry marked complete"
);
}
}