diff --git a/src/cache/base.rs b/src/cache/base.rs index 8f5897f9..8ff0d302 100644 --- a/src/cache/base.rs +++ b/src/cache/base.rs @@ -1,5 +1,9 @@ -use crate::mem_sub_partition::SECTOR_SIZE; -use crate::{address, config, interconn as ic, mem_fetch, mshr, tag_array, Cycle}; +use crate::{ + address, config, interconn as ic, mem_fetch, + mem_sub_partition::SECTOR_SIZE, + mshr::{self, MSHR}, + tag_array, Cycle, +}; use console::style; use std::collections::{HashMap, VecDeque}; use std::sync::{Arc, Mutex}; @@ -36,7 +40,7 @@ pub struct Base { pub miss_queue: VecDeque, pub miss_queue_status: mem_fetch::Status, - pub mshrs: mshr::Table, + pub mshrs: mshr::Table, pub tag_array: tag_array::TagArray<()>, pending: HashMap, @@ -165,7 +169,7 @@ impl Base { let mut evicted = None; let mshr_addr = self.cache_config.mshr_addr(fetch.addr()); - let mshr_hit = self.mshrs.probe(mshr_addr); + let mshr_hit = self.mshrs.get(mshr_addr).is_some(); let mshr_full = self.mshrs.full(mshr_addr); log::debug!( diff --git a/src/cache/data.rs b/src/cache/data.rs index 3f6325e8..d1757ea3 100644 --- a/src/cache/data.rs +++ b/src/cache/data.rs @@ -1,5 +1,4 @@ -use super::{base, event}; -use crate::{address, cache, config, interconn as ic, mem_fetch, tag_array, Cycle}; +use crate::{address, cache, config, interconn as ic, mem_fetch, mshr::MSHR, tag_array, Cycle}; use std::collections::VecDeque; use std::sync::{Arc, Mutex}; @@ -10,7 +9,7 @@ use std::sync::{Arc, Mutex}; /// (the policy used in fermi according to the CUDA manual) #[derive(Debug)] pub struct Data { - pub inner: base::Base, + pub inner: cache::base::Base, /// Specifies type of write allocate request (e.g., L1 or L2) write_alloc_type: mem_fetch::AccessKind, /// Specifies type of writeback request (e.g., L1 or L2) @@ -62,7 +61,7 @@ where cache_index: Option, fetch: &mem_fetch::MemFetch, time: u64, - _events: &mut [event::Event], + _events: &mut [cache::Event], _probe_status: cache::RequestStatus, ) -> cache::RequestStatus { debug_assert_eq!(addr, fetch.addr()); @@ -120,7 +119,7 @@ where // cache_index: usize, fetch: &mem_fetch::MemFetch, time: u64, - _events: &mut [event::Event], + _events: &mut [cache::event::Event], _probe_status: cache::RequestStatus, ) -> cache::RequestStatus { let super::base::Base { @@ -151,9 +150,9 @@ where pub fn send_write_request( &mut self, mut fetch: mem_fetch::MemFetch, - request: event::Event, + request: cache::Event, time: u64, - events: &mut Vec, + events: &mut Vec, ) { log::debug!("data_cache::send_write_request({})", fetch); events.push(request); @@ -171,7 +170,7 @@ where cache_index: Option, fetch: &mem_fetch::MemFetch, time: u64, - events: &mut Vec, + events: &mut Vec, _probe_status: cache::RequestStatus, ) -> cache::RequestStatus { if !self.inner.miss_queue_can_fit(1) { @@ -244,7 +243,7 @@ where // address from the original mf writeback_fetch.tlx_addr.chip = fetch.tlx_addr.chip; writeback_fetch.tlx_addr.sub_partition = fetch.tlx_addr.sub_partition; - let event = event::Event::WriteBackRequestSent { + let event = cache::Event::WriteBackRequestSent { evicted_block: None, }; @@ -269,7 +268,7 @@ where _cache_index: Option, fetch: mem_fetch::MemFetch, time: u64, - events: &mut Vec, + events: &mut Vec, _probe_status: cache::RequestStatus, ) -> cache::RequestStatus { debug_assert_eq!(addr, fetch.addr()); @@ -291,7 +290,7 @@ where } // on miss, generate write through - let event = event::Event::WriteRequestSent; + let event = cache::Event::WriteRequestSent; self.send_write_request(fetch, event, time, events); cache::RequestStatus::MISS } @@ -303,7 +302,7 @@ where cache_index: Option, fetch: mem_fetch::MemFetch, time: u64, - events: &mut Vec, + events: &mut Vec, probe_status: cache::RequestStatus, ) -> cache::RequestStatus { // what exactly is the difference between the addr and the fetch addr? @@ -316,7 +315,7 @@ where // (write miss, read request, write back request) // // Conservatively ensure the worst-case request can be handled this cycle - let mshr_hit = self.inner.mshrs.probe(mshr_addr); + let mshr_hit = self.inner.mshrs.get(mshr_addr).is_some(); let mshr_free = !self.inner.mshrs.full(mshr_addr); let mshr_miss_but_free = !mshr_hit && mshr_free && !self.inner.miss_queue_full(); @@ -344,7 +343,7 @@ where return cache::RequestStatus::RESERVATION_FAIL; } - let event = event::Event::WriteRequestSent; + let event = cache::Event::WriteRequestSent; self.send_write_request(fetch.clone(), event, time, events); let is_write = false; @@ -383,7 +382,7 @@ where is_write_allocate, ); - events.push(event::Event::WriteAllocateSent); + events.push(cache::Event::WriteAllocateSent); if should_miss { // If evicted block is modified and not a write-through @@ -428,7 +427,7 @@ where // is used, so set the right chip address from the original mf writeback_fetch.tlx_addr.chip = fetch.tlx_addr.chip; writeback_fetch.tlx_addr.sub_partition = fetch.tlx_addr.sub_partition; - let event = event::Event::WriteBackRequestSent { + let event = cache::Event::WriteBackRequestSent { evicted_block: Some(evicted), }; @@ -447,7 +446,7 @@ where cache_index: Option, fetch: mem_fetch::MemFetch, time: u64, - events: &mut Vec, + events: &mut Vec, probe_status: cache::RequestStatus, ) -> cache::RequestStatus { let func = match self.inner.cache_config.write_allocate_policy { @@ -475,7 +474,7 @@ where cache_index: Option, fetch: &mem_fetch::MemFetch, time: u64, - events: &mut [event::Event], + events: &mut [cache::Event], probe_status: cache::RequestStatus, ) -> cache::RequestStatus { let func = match self.inner.cache_config.write_policy { @@ -501,7 +500,7 @@ where addr: address, cache_index: Option, fetch: mem_fetch::MemFetch, - events: &mut Vec, + events: &mut Vec, time: u64, ) -> cache::RequestStatus { // dbg!(cache_index, probe_status); @@ -587,7 +586,7 @@ where &mut self, addr: address, fetch: mem_fetch::MemFetch, - events: &mut Vec, + events: &mut Vec, time: u64, ) -> cache::RequestStatus { let super::base::Base { diff --git a/src/lib.rs b/src/lib.rs index bd05c853..5885c964 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -430,10 +430,6 @@ where let start_total = Instant::now(); // int clock_mask = next_clock_domain(); - // fn is_send(_: T) {} - // fn is_sync(_: T) {} - // fn is_par_iter(_: T) {} - // shader core loading (pop from ICNT into core) let start = Instant::now(); if false && self.parallel_simulation { diff --git a/src/mshr.rs b/src/mshr.rs index 14a44a6b..11828754 100644 --- a/src/mshr.rs +++ b/src/mshr.rs @@ -1,7 +1,7 @@ use super::{address, mem_fetch}; use std::collections::{HashMap, VecDeque}; -/// Miss status handlign register kind. +/// Miss status handling register kind. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Kind { TEX_FIFO, // F @@ -10,67 +10,118 @@ pub enum Kind { SECTOR_ASSOC, // S } -#[derive(Debug, Default)] -pub struct Entry { - list: VecDeque, +/// Miss status handling entry. +#[derive(Debug)] +pub struct Entry { + requests: VecDeque, has_atomic: bool, } +impl Default for Entry { + fn default() -> Self { + Self { + requests: VecDeque::new(), + has_atomic: false, + } + } +} + +/// Miss status handling entry. #[derive(Debug)] -pub struct Table { +pub struct Table { num_entries: usize, max_merged: usize, - data: HashMap, + entries: HashMap>, /// If the current response is ready /// /// it may take several cycles to process the merged requests - // current_response_ready: bool, current_response: VecDeque
, } -impl Table { +pub trait MSHR { + // AllEntries() []*MSHREntry + + /// Checks if there is no more space for tracking a new memory access. #[must_use] - pub fn new(num_entries: usize, max_merged: usize) -> Self { - let data = HashMap::with_capacity(2 * num_entries); - Self { - num_entries, - max_merged, - data, - current_response: VecDeque::new(), - } - } + fn full(&self, block_addr: address) -> bool; - /// Checks if there is a pending request to the lower memory level already + /// Get pending requests for a given block address. #[must_use] - pub fn probe(&self, block_addr: address) -> bool { - self.data.contains_key(&block_addr) - } + fn get(&self, block_addr: address) -> Option<&Entry>; - /// Checks if there is space for tracking a new memory access + /// Get pending requests for a given block address. #[must_use] - pub fn full(&self, block_addr: address) -> bool { - match self.data.get(&block_addr) { - Some(entry) => entry.list.len() >= self.max_merged, - None => self.data.len() >= self.num_entries, + fn get_mut(&mut self, block_addr: address) -> Option<&mut Entry>; + + /// Add or merge access. + fn add(&mut self, block_addr: address, fetch: F); + + /// Remove access. + fn remove(&mut self, block_addr: address); + + /// Clear the miss status handling register. + fn clear(&mut self); +} + +impl MSHR for Table { + #[inline] + fn full(&self, block_addr: address) -> bool { + match self.entries.get(&block_addr) { + Some(entry) => entry.requests.len() >= self.max_merged, + None => self.entries.len() >= self.num_entries, } } - /// Add or merge this access - pub fn add(&mut self, block_addr: address, fetch: mem_fetch::MemFetch) { - let entry = self.data.entry(block_addr).or_default(); + #[inline] + fn clear(&mut self) { + self.entries.clear(); + } + + #[inline] + fn get(&self, block_addr: address) -> Option<&Entry> { + self.entries.get(&block_addr) + } + + #[inline] + fn get_mut(&mut self, block_addr: address) -> Option<&mut Entry> { + self.entries.get_mut(&block_addr) + } + + #[inline] + fn add(&mut self, block_addr: address, fetch: mem_fetch::MemFetch) { + let entry = self.entries.entry(block_addr).or_default(); - debug_assert!(entry.list.len() <= self.max_merged); + debug_assert!(entry.requests.len() <= self.max_merged); // indicate that this MSHR entry contains an atomic operation entry.has_atomic |= fetch.is_atomic(); - entry.list.push_back(fetch); - debug_assert!(self.data.len() <= self.num_entries); + entry.requests.push_back(fetch); + debug_assert!(self.entries.len() <= self.num_entries); + } + + #[inline] + fn remove(&mut self, block_addr: address) { + self.entries.remove(&block_addr); + } +} + +impl Table { + #[must_use] + pub fn new(num_entries: usize, max_merged: usize) -> Self { + let entries = HashMap::with_capacity(2 * num_entries); + Self { + num_entries, + max_merged, + entries, + current_response: VecDeque::new(), + } } - // /// check is_read_after_write_pending + /// check is_read_after_write_pending + // #[allow(dead_code)] // pub fn is_read_after_write_pending(&self, block_addr: address) -> bool { // let mut write_found = false; - // for fetch in &self.data[&block_addr].list { + // for fetch in &self.entries[&block_addr].list { // if fetch.is_write() { // // pending write // write_found = true; @@ -87,9 +138,9 @@ impl Table { /// # Returns /// If the ready mshr entry is an atomic pub fn mark_ready(&mut self, block_addr: address, fetch: mem_fetch::MemFetch) -> Option { - let has_atomic = if let Some(entry) = self.data.get_mut(&block_addr) { + let has_atomic = if let Some(entry) = self.entries.get_mut(&block_addr) { self.current_response.push_back(block_addr); - if let Some(old_fetch) = entry.list.iter_mut().find(|f| *f == &fetch) { + if let Some(old_fetch) = entry.requests.iter_mut().find(|f| *f == &fetch) { *old_fetch = fetch; } Some(entry.has_atomic) @@ -101,7 +152,7 @@ impl Table { block_addr, has_atomic ); - debug_assert!(self.current_response.len() <= self.data.len()); + debug_assert!(self.current_response.len() <= self.entries.len()); has_atomic } @@ -117,10 +168,10 @@ impl Table { let Some(block_addr) = self.current_response.front() else { return None; }; - let Some(entry) = self.data.get(block_addr) else { + let Some(entry) = self.entries.get(block_addr) else { return None; }; - Some(&entry.list) + Some(&entry.requests) } /// Returns mutable reference to the next ready accesses @@ -128,30 +179,28 @@ impl Table { let Some(block_addr) = self.current_response.front() else { return None; }; - let Some(entry) = self.data.get_mut(block_addr) else { + let Some(entry) = self.entries.get_mut(block_addr) else { return None; }; - Some(&mut entry.list) + Some(&mut entry.requests) } /// Returns next ready access pub fn next_access(&mut self) -> Option { - // let ready_accesses = self.ready_accesses_mut(); - // debug_assert!(self.has_ready_accesses()); let Some(block_addr) = self.current_response.front() else { return None; }; - let Some(entry) = self.data.get_mut(block_addr) else { + let Some(entry) = self.entries.get_mut(block_addr) else { return None; }; - debug_assert!(!entry.list.is_empty()); - let fetch = entry.list.pop_front(); + debug_assert!(!entry.requests.is_empty()); + let fetch = entry.requests.pop_front(); - let should_remove = entry.list.is_empty(); + let should_remove = entry.requests.is_empty(); if should_remove { - self.data.remove(block_addr); + self.entries.remove(block_addr); self.current_response.pop_front(); } fetch @@ -160,6 +209,7 @@ impl Table { #[cfg(test)] mod tests { + use super::MSHR; use crate::{config, mem_fetch, warp}; #[test] @@ -181,11 +231,11 @@ mod tests { ); let fetch = mem_fetch::MemFetch::new(None, access, &config, 0, 0, 0, 0); let mshr_addr = cache_config.mshr_addr(fetch_addr); - assert!(!mshrs.probe(mshr_addr)); - assert!(!mshrs.probe(mshr_addr)); + assert!(mshrs.get(mshr_addr).is_none()); + assert!(mshrs.get(mshr_addr).is_none()); mshrs.add(mshr_addr, fetch); - assert!(mshrs.probe(mshr_addr)); + assert!(mshrs.get(mshr_addr).is_some()); // TODO: test against bridge here }