use crate::sim::memory::write_log::WriteSeq;
use std::collections::HashMap;
use crate::arch::reservation::LrScRecord;
use crate::arch::translation::{DirtyUpdates, SfenceVmaInfo};
use crate::common::InstSeq;
use crate::exec::compute::vector::shadow::{ElementWrite, VectorWrites};
use crate::exec::execute::CsrWrite;
use crate::exec::signals::ControlSignals;
use crate::isa::csr::CsrAddr;
use crate::isa::instruction::InstSize;
use crate::isa::privileged::Trap;
use crate::isa::reg::RegIdx;
use crate::isa::rvv::VectorConfig;
use crate::uarch::pipeline::exception::ExceptionStage;
use crate::uarch::pipeline::rename::checkpoint::CheckpointId;
use crate::uarch::pipeline::rename::prf::PhysReg;
use crate::uarch::pipeline::rename::vec_prf::VecPhysReg;
#[derive(Clone, Copy, Debug, Default)]
pub struct BpOutcome {
pub taken: bool,
pub mispredicted: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct RobTag(pub u32);
impl RobTag {
#[inline]
pub const fn is_older_than(self, other: Self) -> bool {
(self.0.wrapping_sub(other.0) as i32) < 0
}
#[inline]
pub const fn is_newer_than(self, other: Self) -> bool {
other.is_older_than(self)
}
#[inline]
pub const fn is_older_or_eq(self, other: Self) -> bool {
!other.is_older_than(self)
}
#[must_use]
pub fn age_cmp(self, other: Self) -> std::cmp::Ordering {
(self.0.wrapping_sub(other.0).cast_signed()).cmp(&0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum RobState {
#[default]
Issued,
Completed,
Faulted,
}
#[derive(Clone, Debug, Default)]
pub struct CsrUpdate {
pub addr: CsrAddr,
pub old_val: u64,
pub new_val: u64,
pub applied: bool,
}
impl From<CsrWrite> for CsrUpdate {
fn from(write: CsrWrite) -> Self {
Self { addr: write.addr, old_val: write.old, new_val: write.new, applied: false }
}
}
#[derive(Clone, Debug, Default)]
#[allow(clippy::struct_excessive_bools)]
pub struct RobEntry {
pub tag: RobTag,
pub seq: InstSeq,
pub pc: u64,
pub inst: u32,
pub inst_size: InstSize,
pub rd: RegIdx,
pub result: Option<u64>,
pub ctrl: ControlSignals,
pub state: RobState,
pub trap: Option<Trap>,
pub exception_stage: Option<ExceptionStage>,
pub csr_update: Option<CsrUpdate>,
pub vec_csr_update: Option<VectorConfig>,
pub fault_vstart: Option<u64>,
pub vl_trim: Option<u64>,
pub valid: bool,
pub phys_dst: PhysReg,
pub old_phys_dst: PhysReg,
pub fp_flags: u8,
pub control_resolved: bool,
pub bp_outcome: BpOutcome,
pub bp_target: Option<u64>,
pub dirty_updates: DirtyUpdates,
pub sfence_vma: Option<SfenceVmaInfo>,
pub lr_sc: Option<LrScRecord>,
pub observed: Option<WriteSeq>,
pub checkpoint_id: Option<CheckpointId>,
pub vec_phys_dst: [VecPhysReg; 8],
pub vec_old_phys_dst: [VecPhysReg; 8],
pub vec_dst_count: u8,
pub vxsat: bool,
pub vec_writes: Option<Box<VectorWrites>>,
}
#[derive(Debug)]
pub struct Rob {
entries: Vec<RobEntry>,
head: usize,
tail: usize,
count: usize,
next_tag: u32,
tag_index: HashMap<RobTag, usize>,
vec_config_updates: usize,
}
impl Rob {
pub fn new(capacity: usize) -> Self {
let mut entries = Vec::with_capacity(capacity);
entries.resize_with(capacity, RobEntry::default);
Self {
entries,
head: 0,
tail: 0,
count: 0,
next_tag: 1,
tag_index: HashMap::with_capacity(capacity),
vec_config_updates: 0,
}
}
#[inline]
pub const fn len(&self) -> usize {
self.count
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.count == 0
}
#[inline]
pub const fn is_full(&self) -> bool {
self.count == self.entries.len()
}
#[inline]
pub const fn free_slots(&self) -> usize {
self.entries.len() - self.count
}
#[allow(clippy::too_many_arguments)]
pub fn allocate(
&mut self,
pc: u64,
inst: u32,
inst_size: InstSize,
rd: RegIdx,
ctrl: ControlSignals,
phys_dst: PhysReg,
old_phys_dst: PhysReg,
seq: InstSeq,
) -> Option<RobTag> {
if self.is_full() {
return None;
}
let tag = RobTag(self.next_tag);
self.next_tag = self.next_tag.wrapping_add(1);
if self.next_tag == 0 {
self.next_tag = 1;
}
self.entries[self.tail] = RobEntry {
tag,
seq,
pc,
inst,
inst_size,
rd,
result: None,
ctrl,
state: RobState::Issued,
trap: None,
exception_stage: None,
csr_update: None,
vec_csr_update: None,
fault_vstart: None,
vl_trim: None,
valid: true,
phys_dst,
old_phys_dst,
fp_flags: 0,
control_resolved: false,
bp_outcome: BpOutcome::default(),
bp_target: None,
dirty_updates: DirtyUpdates::NONE,
sfence_vma: None,
lr_sc: None,
observed: None,
checkpoint_id: None,
vec_phys_dst: [VecPhysReg::ZERO; 8],
vec_old_phys_dst: [VecPhysReg::ZERO; 8],
vec_dst_count: 0,
vxsat: false,
vec_writes: None,
};
let _ = self.tag_index.insert(tag, self.tail);
self.tail = (self.tail + 1) % self.entries.len();
self.count += 1;
Some(tag)
}
pub fn complete(&mut self, tag: RobTag, result: u64) {
if let Some(entry) = self.find_entry_mut(tag)
&& entry.state != RobState::Faulted
{
entry.state = RobState::Completed;
entry.result = Some(result);
}
}
pub fn forward(&mut self, tag: RobTag, result: u64) {
if let Some(entry) = self.find_entry_mut(tag)
&& entry.state == RobState::Issued
{
entry.result = Some(result);
}
}
pub fn fault(&mut self, tag: RobTag, trap: Trap, stage: ExceptionStage) {
if let Some(entry) = self.find_entry_mut(tag)
&& entry.state != RobState::Faulted
{
entry.state = RobState::Faulted;
entry.trap = Some(trap);
entry.exception_stage = Some(stage);
}
}
#[must_use]
pub fn is_head(&self, tag: RobTag) -> bool {
self.peek_head().is_some_and(|head| head.tag == tag)
}
pub fn fault_element(&mut self, tag: RobTag, trap: Trap, stage: ExceptionStage, element: u64) {
if let Some(entry) = self.find_entry_mut(tag)
&& entry.state != RobState::Faulted
{
entry.state = RobState::Faulted;
entry.trap = Some(trap);
entry.exception_stage = Some(stage);
entry.fault_vstart = Some(element);
}
}
pub fn set_vl_trim(&mut self, tag: RobTag, vl: u64) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.vl_trim = Some(entry.vl_trim.map_or(vl, |current| current.min(vl)));
}
}
pub fn set_vec_csr_update(&mut self, tag: RobTag, config: VectorConfig) {
if let Some(entry) = self.find_entry_mut(tag) {
let first_update = entry.vec_csr_update.is_none();
entry.vec_csr_update = Some(config);
if first_update {
self.vec_config_updates += 1;
}
}
}
#[must_use]
pub fn youngest_vec_csr_update(&self) -> Option<VectorConfig> {
if self.vec_config_updates == 0 {
return None;
}
let len = self.entries.len();
let mut idx = (self.tail + len - 1) % len;
for _ in 0..self.count {
let entry = &self.entries[idx];
if entry.valid && entry.vec_csr_update.is_some() {
return entry.vec_csr_update;
}
idx = (idx + len - 1) % len;
}
None
}
pub fn set_csr_update(&mut self, tag: RobTag, update: CsrUpdate) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.csr_update = Some(update);
}
}
pub fn set_control_outcome(&mut self, tag: RobTag, outcome: BpOutcome, target: Option<u64>) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.control_resolved = true;
entry.bp_outcome = outcome;
entry.bp_target = target;
}
}
pub fn set_fp_flags(&mut self, tag: RobTag, fp_flags: u8) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.fp_flags |= fp_flags;
}
}
pub fn set_vec_writes(&mut self, tag: RobTag, writes: VectorWrites) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.vec_writes = Some(Box::new(writes));
}
}
pub fn push_vec_element_write(&mut self, tag: RobTag, write: ElementWrite) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.vec_writes.get_or_insert_with(Box::default).elements.push(write);
}
}
pub fn set_vxsat(&mut self, tag: RobTag, val: bool) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.vxsat |= val;
}
}
pub fn set_dirty_updates(&mut self, tag: RobTag, updates: DirtyUpdates) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.dirty_updates = updates;
}
}
pub fn set_sfence_vma(&mut self, tag: RobTag, info: SfenceVmaInfo) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.sfence_vma = Some(info);
}
}
pub fn set_lr_sc(&mut self, tag: RobTag, record: LrScRecord) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.lr_sc = Some(record);
}
}
pub fn set_observed(&mut self, tag: RobTag, seq: WriteSeq) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.observed = Some(seq);
}
}
pub fn set_checkpoint_id(&mut self, tag: RobTag, id: CheckpointId) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.checkpoint_id = Some(id);
}
}
pub fn set_vec_phys_dst(
&mut self,
tag: RobTag,
phys_dst: [VecPhysReg; 8],
old_phys_dst: [VecPhysReg; 8],
count: u8,
) {
if let Some(entry) = self.find_entry_mut(tag) {
entry.vec_phys_dst = phys_dst;
entry.vec_old_phys_dst = old_phys_dst;
entry.vec_dst_count = count;
}
}
pub fn peek_head(&self) -> Option<&RobEntry> {
if self.count == 0 { None } else { Some(&self.entries[self.head]) }
}
pub fn commit_head(&mut self) -> Option<RobEntry> {
if self.count == 0 {
return None;
}
let entry = &self.entries[self.head];
if entry.state == RobState::Issued {
return None;
}
let committed = self.entries[self.head].clone();
let _ = self.tag_index.remove(&committed.tag);
if committed.vec_csr_update.is_some() {
self.vec_config_updates -= 1;
}
self.entries[self.head].valid = false;
self.head = (self.head + 1) % self.entries.len();
self.count -= 1;
Some(committed)
}
pub fn flush_all(&mut self) {
for entry in &mut self.entries {
entry.valid = false;
}
self.tag_index.clear();
self.head = 0;
self.tail = 0;
self.count = 0;
self.vec_config_updates = 0;
}
pub fn flush_after(&mut self, tag: RobTag) {
if self.count == 0 {
return;
}
let mut idx = self.head;
let mut found = false;
for _ in 0..self.count {
if self.entries[idx].tag == tag {
found = true;
break;
}
idx = (idx + 1) % self.entries.len();
}
if !found {
return;
}
let keep_idx = (idx + 1) % self.entries.len();
if keep_idx == self.tail {
return;
}
let mut remove_idx = keep_idx;
while remove_idx != self.tail {
let _ = self.tag_index.remove(&self.entries[remove_idx].tag);
if self.entries[remove_idx].vec_csr_update.is_some() {
self.vec_config_updates -= 1;
}
self.entries[remove_idx].valid = false;
remove_idx = (remove_idx + 1) % self.entries.len();
}
self.tail = keep_idx;
self.count = 0;
let mut i = self.head;
loop {
if i == self.tail {
break;
}
if self.entries[i].valid {
self.count += 1;
}
i = (i + 1) % self.entries.len();
}
}
pub fn prev_tag_of(&self, tag: RobTag) -> Option<RobTag> {
if self.count == 0 {
return None;
}
let mut prev: Option<RobTag> = None;
let mut idx = self.head;
for _ in 0..self.count {
let entry = &self.entries[idx];
if entry.valid {
if entry.tag == tag {
return prev;
}
prev = Some(entry.tag);
}
idx = (idx + 1) % self.entries.len();
}
None
}
fn find_entry_mut(&mut self, tag: RobTag) -> Option<&mut RobEntry> {
let idx = *self.tag_index.get(&tag)?;
let entry = &mut self.entries[idx];
if entry.valid { Some(entry) } else { None }
}
pub fn for_each_valid(&self, mut f: impl FnMut(&RobEntry)) {
if self.count == 0 {
return;
}
let mut idx = self.head;
for _ in 0..self.count {
if self.entries[idx].valid {
f(&self.entries[idx]);
}
idx = (idx + 1) % self.entries.len();
}
}
pub fn find_entry(&self, tag: RobTag) -> Option<&RobEntry> {
let idx = *self.tag_index.get(&tag)?;
let entry = &self.entries[idx];
if entry.valid { Some(entry) } else { None }
}
pub fn iter_in_order(&self) -> impl Iterator<Item = &RobEntry> {
let cap = self.entries.len();
let head = self.head;
let count = self.count;
(0..count).filter_map(move |i| {
let idx = (head + i) % cap;
let e = &self.entries[idx];
if e.valid { Some(e) } else { None }
})
}
pub fn iter_after(&self, keep_tag: RobTag) -> impl Iterator<Item = &RobEntry> {
self.iter_all().filter(move |e| e.tag.is_newer_than(keep_tag))
}
pub fn iter_all(&self) -> impl Iterator<Item = &RobEntry> {
let entries = &self.entries;
let head = self.head;
(0..self.count).map(move |i| &entries[(head + i) % entries.len()]).filter(|e| e.valid)
}
pub fn fence_pred_satisfied(&self, tag: RobTag, pred_r: bool, pred_w: bool) -> bool {
if !pred_r && !pred_w {
return true;
}
if self.count == 0 {
return true;
}
let mut idx = self.head;
for _ in 0..self.count {
let entry = &self.entries[idx];
if entry.valid {
if entry.tag == tag {
return true;
}
if entry.state == RobState::Issued {
let dominated = (pred_r && entry.ctrl.reads_memory())
|| (pred_w && entry.ctrl.writes_memory());
if dominated {
return false;
}
}
}
idx = (idx + 1) % self.entries.len();
}
true
}
pub fn has_fence_blocking(&self, tag: RobTag, is_load: bool, is_store: bool) -> bool {
if self.count == 0 || (!is_load && !is_store) {
return false;
}
let mut idx = self.head;
for _ in 0..self.count {
let entry = &self.entries[idx];
if entry.valid {
if entry.tag == tag {
return false;
}
if is_load && entry.ctrl.acquire && entry.state == RobState::Issued {
return true;
}
if entry.ctrl.system_op == crate::isa::op::SystemOp::Fence {
let succ_bits = ((entry.inst >> 20) & 0xF) as u8;
let succ_r = succ_bits & 0b0010 != 0;
let succ_w = succ_bits & 0b0001 != 0;
let blocked = (is_load && succ_r) || (is_store && succ_w);
if blocked {
return true;
}
}
}
idx = (idx + 1) % self.entries.len();
}
false
}
}
#[cfg(test)]
mod tests;