use std::cmp::Ordering;
use std::cmp::Reverse;
use std::collections::BinaryHeap;
use crate::sim::components::ComponentId;
use crate::sim::packet::Packet;
#[derive(Clone, Debug)]
pub struct Event {
pub fire_at: u64,
pub seq: u64,
pub target: ComponentId,
pub source: ComponentId,
pub packet: Packet,
}
impl Event {
#[inline]
const fn ordering_key(&self) -> (u64, u64) {
(self.fire_at, self.seq)
}
}
impl PartialEq for Event {
fn eq(&self, other: &Self) -> bool {
self.ordering_key() == other.ordering_key()
}
}
impl Eq for Event {}
impl PartialOrd for Event {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Event {
fn cmp(&self, other: &Self) -> Ordering {
self.ordering_key().cmp(&other.ordering_key())
}
}
#[derive(Debug, Default)]
pub struct EventQueue {
heap: BinaryHeap<Reverse<Event>>,
next_seq: u64,
}
impl EventQueue {
#[inline]
pub const fn new() -> Self {
Self { heap: BinaryHeap::new(), next_seq: 0 }
}
pub fn schedule(
&mut self,
fire_at: u64,
target: ComponentId,
source: ComponentId,
packet: Packet,
) {
let event = Event { fire_at, seq: self.next_seq, target, source, packet };
self.next_seq += 1;
self.heap.push(Reverse(event));
}
pub fn pop_ready(&mut self, cycle: u64) -> Option<Event> {
match self.heap.peek() {
Some(Reverse(top)) if top.fire_at <= cycle => self.heap.pop().map(|Reverse(e)| e),
_ => None,
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.heap.is_empty()
}
pub fn pending(&self) -> impl Iterator<Item = &Event> {
self.heap.iter().map(|Reverse(event)| event)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::{LineAddr, PhysAddr};
use crate::sim::components::{CacheId, PipelineId, ReqId};
use crate::sim::packet::{HitLevel, MemRespData, MesiState};
fn make_resp(req_id: u64) -> Packet {
Packet::MemResp {
req_id: ReqId::new(req_id),
line_addr: LineAddr::from_phys(PhysAddr::new(0), 64),
data: MemRespData::Small(0),
hit_level: HitLevel::L1,
state: MesiState::Exclusive,
}
}
#[test]
fn pop_ready_respects_cycle() {
let mut q = EventQueue::new();
let src = ComponentId::Pipeline(PipelineId::new(0));
let dst = ComponentId::Cache(CacheId::new(0));
q.schedule(10, dst, src, make_resp(1));
assert!(q.pop_ready(5).is_none());
let evt = q.pop_ready(10).expect("event ready at cycle 10");
assert_eq!(evt.fire_at, 10);
}
#[test]
fn events_ordered_by_fire_at_then_seq() {
let mut q = EventQueue::new();
let src = ComponentId::Pipeline(PipelineId::new(0));
let dst = ComponentId::Cache(CacheId::new(0));
q.schedule(20, dst, src, make_resp(20));
q.schedule(10, dst, src, make_resp(10));
q.schedule(10, dst, src, make_resp(11));
let a = q.pop_ready(100).unwrap();
assert_eq!(a.fire_at, 10);
match a.packet {
Packet::MemResp { req_id, .. } => assert_eq!(req_id, ReqId::new(10)),
_ => panic!("wrong packet"),
}
let b = q.pop_ready(100).unwrap();
assert_eq!(b.fire_at, 10);
match b.packet {
Packet::MemResp { req_id, .. } => assert_eq!(req_id, ReqId::new(11)),
_ => panic!("wrong packet"),
}
let c = q.pop_ready(100).unwrap();
assert_eq!(c.fire_at, 20);
assert!(q.is_empty());
}
}