use crate::common::{HartId, PhysAddr};
use crate::config::Config;
use crate::sim::components::{ComponentId, MemCtrlId, PipelineId, ReqId};
use crate::sim::events::EventQueue;
use crate::sim::handle::{Handle, HandleCtx};
use crate::sim::memory::GlobalMemory;
use crate::sim::packet::WriteOrigin;
use crate::sim::packet::{AccessSize, MemOp, Packet, WriteData};
use crate::sim::stats::Stats;
use crate::soc::memory::controller::{Bandwidth, DramConfig, DramController, SimpleController};
use std::num::NonZeroU64;
fn read_latency<H: Handle>(ctrl: &mut H, paddr: u64, cycle: u64) -> u64 {
let req_id = ReqId::new(u64::MAX);
let mut queue = EventQueue::new();
let mut stats = Stats::new();
let mut memory = GlobalMemory::new(None, 1, 64);
let config = Config::default();
let mut ctx = HandleCtx {
scheduler: &mut queue,
stats: &mut stats,
memory: &mut memory,
config: &config,
cycle,
self_id: ComponentId::MemCtrl(MemCtrlId::new(0)),
};
ctrl.handle(
Packet::MemReq {
req_id,
paddr: PhysAddr::new(paddr),
vaddr: None,
pc: None,
size: AccessSize::B8,
op: MemOp::Read,
},
ComponentId::Pipeline(PipelineId::new(0)),
&mut ctx,
);
while let Some(event) = queue.pop_ready(u64::MAX) {
if let Packet::MemResp { req_id: rid, .. } = event.packet
&& rid == req_id
{
return event.fire_at - cycle;
}
}
panic!("no MemResp scheduled");
}
fn write_latency<H: Handle>(ctrl: &mut H, paddr: u64, cycle: u64) -> u64 {
let req_id = ReqId::new(u64::MAX);
let mut queue = EventQueue::new();
let mut stats = Stats::new();
let mut memory = GlobalMemory::new(None, 1, 64);
let config = Config::default();
let mut ctx = HandleCtx {
scheduler: &mut queue,
stats: &mut stats,
memory: &mut memory,
config: &config,
cycle,
self_id: ComponentId::MemCtrl(MemCtrlId::new(0)),
};
ctrl.handle(
Packet::MemReq {
req_id,
paddr: PhysAddr::new(paddr),
vaddr: None,
pc: None,
size: AccessSize::B8,
op: MemOp::Write {
data: WriteData::Small(0),
origin: WriteOrigin::Hart(HartId::new(0)),
},
},
ComponentId::Pipeline(PipelineId::new(0)),
&mut ctx,
);
while let Some(event) = queue.pop_ready(u64::MAX) {
if let Packet::MemResp { req_id: rid, .. } = event.packet
&& rid == req_id
{
return event.fire_at - cycle;
}
}
panic!("no MemResp scheduled");
}
fn simple_with_bandwidth(latency: u64, bytes_per_cycle: u64) -> SimpleController {
let bandwidth = Bandwidth::new(NonZeroU64::new(bytes_per_cycle).unwrap(), 1);
SimpleController::new(latency, bandwidth)
}
fn simple(latency: u64) -> SimpleController {
simple_with_bandwidth(latency, 64)
}
fn line_read_latency(ctrl: &mut SimpleController, paddr: u64, cycle: u64) -> u64 {
let req_id = ReqId::new(u64::MAX);
let mut queue = EventQueue::new();
let mut stats = Stats::new();
let mut memory = GlobalMemory::new(None, 1, 64);
let config = Config::default();
let mut ctx = HandleCtx {
scheduler: &mut queue,
stats: &mut stats,
memory: &mut memory,
config: &config,
cycle,
self_id: ComponentId::MemCtrl(MemCtrlId::new(0)),
};
ctrl.handle(
Packet::MemReq {
req_id,
paddr: PhysAddr::new(paddr),
vaddr: None,
pc: None,
size: AccessSize::Line,
op: MemOp::Read,
},
ComponentId::Pipeline(PipelineId::new(0)),
&mut ctx,
);
while let Some(event) = queue.pop_ready(u64::MAX) {
if let Packet::MemResp { req_id: rid, .. } = event.packet
&& rid == req_id
{
return event.fire_at - cycle;
}
}
panic!("no MemResp scheduled");
}
fn dram_no_refresh(t_cas: u64, t_ras: u64, t_pre: u64) -> DramController {
DramController::new(DramConfig {
t_cas,
t_ras,
t_pre,
t_rrd: 4,
num_banks: 8,
row_size_bytes: 2048,
t_refi: 0,
t_rfc: 0,
})
}
fn dram_default() -> DramController {
dram_no_refresh(5, 10, 8)
}
fn addr(bank: usize, row_group: u64) -> u64 {
(row_group * 8 + bank as u64) * 2048
}
#[test]
fn simple_controller_fixed_latency() {
let mut ctrl = simple(10);
assert_eq!(read_latency(&mut ctrl, 0x1000, 0), 10);
assert_eq!(read_latency(&mut ctrl, 0x2000, 100), 10);
assert_eq!(read_latency(&mut ctrl, 0x3000, 200), 10);
}
#[test]
fn simple_controller_zero_latency() {
let mut ctrl = simple(0);
assert_eq!(read_latency(&mut ctrl, 0, 0), 0);
}
#[test]
fn simple_controller_write_same_as_read() {
let mut ctrl = simple(15);
assert_eq!(write_latency(&mut ctrl, 0x1000, 0), 15);
}
#[test]
fn simple_controller_requests_wait_for_the_bandwidth() {
let mut ctrl = simple_with_bandwidth(10, 8);
assert_eq!(line_read_latency(&mut ctrl, 0x1000, 0), 10, "an idle controller answers at once");
assert_eq!(line_read_latency(&mut ctrl, 0x2000, 0), 18, "the second line waits 64 / 8 cycles");
assert_eq!(line_read_latency(&mut ctrl, 0x3000, 40), 10, "the controller is free again");
}
#[test]
fn dram_first_access_full_latency() {
let mut ctrl = dram_default();
let lat = read_latency(&mut ctrl, addr(0, 0), 0);
assert_eq!(lat, 10 + 5);
}
#[test]
fn dram_row_buffer_hit() {
let mut ctrl = dram_default();
let _ = read_latency(&mut ctrl, addr(0, 0), 0);
let lat = read_latency(&mut ctrl, addr(0, 0) + 64, 100);
assert_eq!(lat, 5);
}
#[test]
fn dram_row_buffer_conflict_same_bank() {
let mut ctrl = dram_default();
let _ = read_latency(&mut ctrl, addr(0, 0), 0);
let lat = read_latency(&mut ctrl, addr(0, 1), 100);
assert_eq!(lat, 8 + 10 + 5);
}
#[test]
fn dram_independent_banks_no_conflict() {
let mut ctrl = dram_default();
let _ = read_latency(&mut ctrl, addr(0, 0), 0);
let lat = read_latency(&mut ctrl, addr(1, 0), 100);
assert_eq!(lat, 10 + 5);
}