use crate::common::LineAddr;
use crate::config::InclusionPolicy;
use crate::sim::packet::Packet;
use crate::soc::cache::Cache;
use crate::soc::coherence::fabric::CoherenceFabric;
use crate::system::coherence_audit::{self, Violation};
use crate::system::state::SystemState;
#[must_use]
pub(crate) fn audit(state: &SystemState) -> Vec<Violation> {
let mut violations = coherence_audit::audit(state);
let llc = &state.uncore.l3_cache;
let mut in_flight =
state.uncore.coherence.as_ref().map(CoherenceFabric::lines_in_flight).unwrap_or_default();
in_flight.extend(llc.lines_in_flight());
for core in state.cores.iter().map(|core| &core.units) {
for cache in [&core.l1_i_cache, &core.l1_d_cache, &core.l2_cache] {
in_flight.extend(cache.lines_in_flight());
}
}
in_flight.extend(lines_on_their_way(state));
let busy = |line: &LineAddr| in_flight.contains(line);
violations.extend(bookkeeping(llc));
for core in state.cores.iter().map(|core| &core.units) {
for cache in [&core.l1_i_cache, &core.l1_d_cache, &core.l2_cache] {
violations.extend(bookkeeping(cache));
}
let uppers = [&core.l1_i_cache, &core.l1_d_cache];
violations.extend(level_faults(&uppers, &core.l2_cache, &busy));
if state.uncore.coherence.is_none() {
violations.extend(level_faults(&[&core.l2_cache], llc, &busy));
}
}
violations
}
fn lines_on_their_way(state: &SystemState) -> Vec<LineAddr> {
let line_bytes =
state.cores.first().map_or(64, |core| core.units.l1_d_cache.line_bytes()) as u64;
state
.uncore
.event_queue
.pending()
.filter_map(|event| match &event.packet {
Packet::MemReq { paddr, .. } => Some(LineAddr::from_phys(*paddr, line_bytes)),
Packet::MemResp { line_addr, .. }
| Packet::Probe { line_addr, .. }
| Packet::CacheInval { line_addr } => Some(*line_addr),
Packet::Coh(msg) => Some(msg.line()),
_ => None,
})
.collect()
}
fn bookkeeping(cache: &Cache) -> impl Iterator<Item = Violation> + '_ {
cache
.bookkeeping_faults()
.into_iter()
.map(|(line, what)| Violation { line, what: format!("cache {:?}: {what}", cache.id) })
}
fn level_faults(
uppers: &[&Cache],
lower: &Cache,
busy: &impl Fn(&LineAddr) -> bool,
) -> Vec<Violation> {
let mut violations = Vec::new();
if !lower.is_enabled() {
return violations;
}
let policy = lower.inclusion_of_upper_levels();
for upper in uppers.iter().filter(|upper| upper.is_enabled()) {
for (line, _) in upper.held_lines() {
if busy(&line) {
continue;
}
let below = lower.contains(line.val());
let what = match (policy, below) {
(InclusionPolicy::Inclusive, false) => {
Some("held above a level inclusive of it that does not hold it")
}
(InclusionPolicy::Exclusive, true) => {
Some("held both above and in a level exclusive of it")
}
(_, true) if !lower.recorded_holders(line).contains(&upper.component()) => {
Some("held above a level that does not record the copy")
}
_ => None,
};
if let Some(what) = what {
violations.push(Violation {
line,
what: format!("caches {:?} and {:?}: {what}", upper.id, lower.id),
});
}
}
}
violations
}