use std::collections::VecDeque;
use crate::common::{LineAddr, PhysAddr};
use crate::sim::components::{BankGroupId, ComponentId, RankId, ReqId, RowId};
use crate::sim::packet::{AccessSize, MemOp};
use crate::soc::memory::address::DramLocation;
use crate::soc::memory::ddr5::stats::{BankCounters, SubchannelCounters};
#[derive(Clone, Copy, Debug)]
pub struct Bank {
pub open_row: Option<RowId>,
pub state: BankState,
pub last_activate: u64,
pub last_precharge: u64,
pub last_read_cmd: u64,
pub last_read_end: u64,
pub last_write_cmd: u64,
pub last_write_end: u64,
pub refresh_end: u64,
pub counters: BankCounters,
}
impl Bank {
#[must_use]
pub const fn new() -> Self {
Self {
open_row: None,
state: BankState::Idle,
last_activate: 0,
last_precharge: 0,
last_read_cmd: 0,
last_read_end: 0,
last_write_cmd: 0,
last_write_end: 0,
refresh_end: 0,
counters: BankCounters::new(),
}
}
}
impl Default for Bank {
fn default() -> Self {
Self::new()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BankState {
Idle,
Active,
Precharging,
Refreshing,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PowerState {
Active,
PowerDown {
since: u64,
with_open_rows: bool,
},
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RefreshPhase {
Idle,
Pending {
bank_mask: u64,
duration: u64,
},
}
#[derive(Clone, Debug)]
pub struct Rank {
pub banks: Vec<Bank>,
pub faw: [u64; 4],
pub faw_slot: usize,
pub faw_populated: u8,
pub next_refresh: u64,
pub refresh_phase: RefreshPhase,
pub refresh_seq: u64,
pub last_command_cycle: u64,
pub last_precharge: u64,
pub power: PowerState,
pub power_up_at: u64,
}
impl Rank {
#[must_use]
pub fn new(bank_count: usize, first_refresh: u64) -> Self {
Self {
banks: vec![Bank::new(); bank_count],
faw: [0; 4],
faw_slot: 0,
faw_populated: 0,
next_refresh: first_refresh,
refresh_phase: RefreshPhase::Idle,
refresh_seq: 0,
last_command_cycle: 0,
last_precharge: 0,
power: PowerState::Active,
power_up_at: 0,
}
}
#[must_use]
pub const fn command_floor(&self) -> u64 {
if self.power_up_at > self.last_command_cycle {
self.power_up_at
} else {
self.last_command_cycle
}
}
#[must_use]
pub fn has_open_row(&self) -> bool {
self.banks.iter().any(|b| b.state == BankState::Active)
}
#[must_use]
pub fn bank_held_for_refresh(&self, bank_index: usize) -> bool {
let pending = match self.refresh_phase {
RefreshPhase::Idle => false,
RefreshPhase::Pending { bank_mask, .. } => {
bank_index < 64 && (bank_mask >> bank_index) & 1 == 1
}
};
pending || self.banks[bank_index].state == BankState::Refreshing
}
pub const fn record_activate(&mut self, cycle: u64, _t_faw: u64) {
self.faw[self.faw_slot] = cycle;
self.faw_slot = (self.faw_slot + 1) % 4;
if self.faw_populated < 4 {
self.faw_populated += 1;
}
}
#[must_use]
pub const fn earliest_activate_faw(&self, t_faw: u64) -> u64 {
if self.faw_populated < 4 {
return 0;
}
self.faw[self.faw_slot] + t_faw
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BusOp {
Read,
Write,
None,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum WriteDrainState {
Filling,
Draining,
}
#[derive(Clone, Debug)]
pub struct PendingReq {
pub req_id: ReqId,
pub arrival_cycle: u64,
pub paddr: PhysAddr,
pub line: LineAddr,
pub loc: DramLocation,
pub size: AccessSize,
pub op: MemOp,
pub source: ComponentId,
pub scrub: bool,
pub activated: bool,
}
#[derive(Clone, Debug)]
pub struct Subchannel {
pub ranks: Vec<Rank>,
pub inbound: VecDeque<PendingReq>,
pub read_queue: VecDeque<PendingReq>,
pub write_queue: VecDeque<PendingReq>,
pub drain_state: WriteDrainState,
pub writes_this_drain: usize,
pub last_command_cycle: u64,
pub last_data_end: u64,
pub last_bus_rank: Option<RankId>,
pub last_data_op: BusOp,
pub last_read_cmd: u64,
pub last_read_end: u64,
pub last_write_cmd: u64,
pub last_write_end: u64,
pub last_column_bg: Option<BankGroupId>,
pub counters: SubchannelCounters,
}
impl Subchannel {
#[must_use]
pub fn new(rank_count: usize, bank_count: usize, refresh_interval: u64) -> Self {
Self {
ranks: fresh_ranks(rank_count, bank_count, refresh_interval, 0),
inbound: VecDeque::new(),
read_queue: VecDeque::new(),
write_queue: VecDeque::new(),
drain_state: WriteDrainState::Filling,
writes_this_drain: 0,
last_command_cycle: 0,
last_data_end: 0,
last_bus_rank: None,
last_data_op: BusOp::None,
last_read_cmd: 0,
last_read_end: 0,
last_write_cmd: 0,
last_write_end: 0,
last_column_bg: None,
counters: SubchannelCounters::default(),
}
}
pub fn restart_at(&mut self, origin: u64, refresh_interval: u64) {
let bank_count = self.ranks.first().map_or(0, |rank| rank.banks.len());
self.ranks = fresh_ranks(self.ranks.len(), bank_count, refresh_interval, origin);
self.drain_state = WriteDrainState::Filling;
self.writes_this_drain = 0;
self.last_command_cycle = 0;
self.last_data_end = 0;
self.last_bus_rank = None;
self.last_data_op = BusOp::None;
self.last_read_cmd = 0;
self.last_read_end = 0;
self.last_write_cmd = 0;
self.last_write_end = 0;
self.last_column_bg = None;
}
}
fn fresh_ranks(
rank_count: usize,
bank_count: usize,
refresh_interval: u64,
origin: u64,
) -> Vec<Rank> {
let ranks_as_u64 = u64::try_from(rank_count).unwrap_or(u64::MAX);
let stagger = refresh_interval.checked_div(ranks_as_u64).unwrap_or(0);
(0..rank_count)
.map(|r| {
let offset = u64::try_from(r).unwrap_or(0) * stagger;
Rank::new(bank_count, origin + refresh_interval + offset)
})
.collect()
}
#[derive(Clone, Debug)]
pub struct DramChannel {
pub subchannels: Vec<Subchannel>,
}
impl DramChannel {
#[must_use]
pub fn new(
subchannel_count: usize,
rank_count: usize,
bank_count: usize,
refresh_interval: u64,
) -> Self {
Self {
subchannels: (0..subchannel_count)
.map(|_| Subchannel::new(rank_count, bank_count, refresh_interval))
.collect(),
}
}
}