use crate::common::{CoreId, LineAddr};
use crate::sim::components::ReqId;
use crate::sim::packet::{Maintenance, MesiState};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Node {
Core(CoreId),
Home,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum MsgClass {
Request,
Snoop,
Response,
Data,
}
impl MsgClass {
pub const ALL: [Self; 4] = [Self::Request, Self::Snoop, Self::Response, Self::Data];
#[must_use]
pub const fn index(self) -> usize {
match self {
Self::Request => 0,
Self::Snoop => 1,
Self::Response => 2,
Self::Data => 3,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReqKind {
ReadShared,
ReadUnique,
CleanUnique,
WriteBack {
dirty: bool,
},
Evict,
Maintain {
op: Maintenance,
dirty: bool,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SnoopKind {
Shared,
Unique,
Invalid,
Clean,
MakeInvalid,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CoherenceMsg {
Req {
txn: ReqId,
line: LineAddr,
kind: ReqKind,
requester: CoreId,
},
Snoop {
txn: ReqId,
line: LineAddr,
kind: SnoopKind,
target: CoreId,
},
SnoopResp {
txn: ReqId,
line: LineAddr,
from: CoreId,
had_copy: bool,
dirty: bool,
},
CompData {
txn: ReqId,
line: LineAddr,
to: CoreId,
state: MesiState,
},
Comp {
txn: ReqId,
line: LineAddr,
to: CoreId,
state: MesiState,
},
CompAck {
txn: ReqId,
line: LineAddr,
from: CoreId,
},
NoSnp {
txn: ReqId,
line: LineAddr,
requester: CoreId,
bytes: usize,
},
NoSnpData {
txn: ReqId,
line: LineAddr,
to: CoreId,
bytes: usize,
},
}
impl CoherenceMsg {
#[must_use]
pub const fn class(self) -> MsgClass {
match self {
Self::Req { .. } | Self::NoSnp { .. } => MsgClass::Request,
Self::Snoop { .. } => MsgClass::Snoop,
Self::SnoopResp { dirty: false, .. } | Self::Comp { .. } | Self::CompAck { .. } => {
MsgClass::Response
}
Self::SnoopResp { dirty: true, .. }
| Self::CompData { .. }
| Self::NoSnpData { .. } => MsgClass::Data,
}
}
#[must_use]
pub const fn destination(self) -> Node {
match self {
Self::Req { .. }
| Self::SnoopResp { .. }
| Self::CompAck { .. }
| Self::NoSnp { .. } => Node::Home,
Self::Snoop { target, .. } => Node::Core(target),
Self::CompData { to, .. } | Self::Comp { to, .. } | Self::NoSnpData { to, .. } => {
Node::Core(to)
}
}
}
#[must_use]
pub const fn bytes(self, line_bytes: usize) -> usize {
const HEADER: usize = 8;
match self {
Self::CompData { .. }
| Self::SnoopResp { dirty: true, .. }
| Self::Req { kind: ReqKind::WriteBack { dirty: true }, .. }
| Self::Req { kind: ReqKind::Maintain { dirty: true, .. }, .. } => HEADER + line_bytes,
Self::NoSnp { bytes, .. } | Self::NoSnpData { bytes, .. } => HEADER + bytes,
_ => HEADER,
}
}
#[cfg(test)]
#[must_use]
pub const fn txn(self) -> ReqId {
match self {
Self::Req { txn, .. }
| Self::Snoop { txn, .. }
| Self::SnoopResp { txn, .. }
| Self::CompData { txn, .. }
| Self::Comp { txn, .. }
| Self::CompAck { txn, .. }
| Self::NoSnp { txn, .. }
| Self::NoSnpData { txn, .. } => txn,
}
}
#[must_use]
pub const fn line(self) -> LineAddr {
match self {
Self::Req { line, .. }
| Self::Snoop { line, .. }
| Self::SnoopResp { line, .. }
| Self::CompData { line, .. }
| Self::Comp { line, .. }
| Self::CompAck { line, .. }
| Self::NoSnp { line, .. }
| Self::NoSnpData { line, .. } => line,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::PhysAddr;
#[test]
fn classes_destinations_and_sizes() {
let line = LineAddr::from_phys(PhysAddr::new(0x1000), 64);
let req = CoherenceMsg::Req {
txn: ReqId::new(1),
line,
kind: ReqKind::ReadShared,
requester: CoreId::new(2),
};
assert_eq!(req.class(), MsgClass::Request);
assert_eq!(req.destination(), Node::Home);
assert_eq!(req.bytes(64), 8);
let data = CoherenceMsg::CompData {
txn: ReqId::new(1),
line,
to: CoreId::new(2),
state: MesiState::Shared,
};
assert_eq!(data.class(), MsgClass::Data);
assert_eq!(data.destination(), Node::Core(CoreId::new(2)));
assert_eq!(data.bytes(64), 72);
let snoop = CoherenceMsg::Snoop {
txn: ReqId::new(3),
line,
kind: SnoopKind::Unique,
target: CoreId::new(0),
};
assert_eq!(snoop.class(), MsgClass::Snoop);
assert_eq!(snoop.destination(), Node::Core(CoreId::new(0)));
}
#[test]
fn a_modified_copy_travels_with_its_line() {
let line = LineAddr::from_phys(PhysAddr::new(0x1000), 64);
let (txn, core) = (ReqId::new(1), CoreId::new(0));
let writeback = |dirty| CoherenceMsg::Req {
txn,
line,
kind: ReqKind::WriteBack { dirty },
requester: core,
};
let answer =
|dirty| CoherenceMsg::SnoopResp { txn, line, from: core, had_copy: true, dirty };
assert_eq!((writeback(true).bytes(64), writeback(false).bytes(64)), (72, 8));
assert_eq!((answer(true).bytes(64), answer(false).bytes(64)), (72, 8));
assert_eq!(
(answer(true).class(), answer(false).class()),
(MsgClass::Data, MsgClass::Response)
);
assert_eq!(writeback(true).class(), MsgClass::Request);
}
}