use crate::common::{CoreId, HartId};
use crate::sim::components::{CacheId, MemCtrlId, PipelineId};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PrivateCache {
L1I,
L1D,
L2,
}
const PRIVATE_CACHES_PER_CORE: u32 = 3;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoreTopology {
pub core_id: CoreId,
pub hart_ids: Vec<HartId>,
pub pipeline_id: PipelineId,
pub l1i: CacheId,
pub l1d: CacheId,
pub l2: CacheId,
}
impl CoreTopology {
#[must_use]
pub const fn cache(&self, which: PrivateCache) -> CacheId {
match which {
PrivateCache::L1I => self.l1i,
PrivateCache::L1D => self.l1d,
PrivateCache::L2 => self.l2,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CacheSlot {
Private {
core: CoreId,
which: PrivateCache,
},
Llc,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Topology {
pub cores: Vec<CoreTopology>,
pub llc: CacheId,
pub mem_ctrls: Vec<MemCtrlId>,
}
impl Topology {
#[must_use]
pub fn single_threaded_cores(core_count: usize) -> Self {
assert!(core_count > 0, "a system needs at least one core");
let cores = (0..core_count)
.map(|c| {
let c32 = u32::try_from(c).unwrap_or(u32::MAX);
let base = c32 * PRIVATE_CACHES_PER_CORE;
CoreTopology {
core_id: CoreId::new(c32),
hart_ids: vec![HartId::new(c32)],
pipeline_id: PipelineId::new(c32),
l1i: CacheId::new(base),
l1d: CacheId::new(base + 1),
l2: CacheId::new(base + 2),
}
})
.collect();
let llc =
CacheId::new(u32::try_from(core_count).unwrap_or(u32::MAX) * PRIVATE_CACHES_PER_CORE);
Self { cores, llc, mem_ctrls: vec![MemCtrlId::new(0)] }
}
#[must_use]
pub fn hart_count(&self) -> usize {
self.cores.iter().map(|c| c.hart_ids.len()).sum()
}
#[must_use]
pub fn core_of_hart(&self, hart: HartId) -> Option<CoreId> {
self.cores.iter().find(|c| c.hart_ids.contains(&hart)).map(|c| c.core_id)
}
#[must_use]
pub fn locate_cache(&self, cache: CacheId) -> Option<CacheSlot> {
if cache == self.llc {
return Some(CacheSlot::Llc);
}
let index = cache.val() / PRIVATE_CACHES_PER_CORE;
let core = self.cores.get(index as usize)?;
let which = match cache.val() % PRIVATE_CACHES_PER_CORE {
0 => PrivateCache::L1I,
1 => PrivateCache::L1D,
_ => PrivateCache::L2,
};
Some(CacheSlot::Private { core: core.core_id, which })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ids_are_dense_and_the_llc_follows_the_last_core() {
let t = Topology::single_threaded_cores(2);
assert_eq!(t.cores[1].l1i, CacheId::new(3));
assert_eq!(t.cores[1].l2, CacheId::new(5));
assert_eq!(t.llc, CacheId::new(6));
assert_eq!(
t.locate_cache(CacheId::new(4)),
Some(CacheSlot::Private { core: CoreId::new(1), which: PrivateCache::L1D })
);
assert_eq!(t.locate_cache(CacheId::new(6)), Some(CacheSlot::Llc));
assert_eq!(t.locate_cache(CacheId::new(7)), None);
assert_eq!(t.core_of_hart(HartId::new(1)), Some(CoreId::new(1)));
assert_eq!(t.hart_count(), 2);
}
}