// Copyright 2015-2016 David Li // This file is part of rustv. // rustv is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // rustv is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with rustv. If not, see . use std::rc::Rc; use std::cell::RefCell; use isa; use memory::{MemoryError, MemoryInterface, Result, SharedMemory}; pub struct CacheMetadata { /// How many sets are in the cache pub num_sets: usize, /// How many ways are in a set pub num_ways: usize, /// How many words are in a block/line pub num_block_words: usize, /// The tags currently in the cache, in order of set, then way pub tags: Vec>, } pub trait CacheInterface : MemoryInterface { fn cache_metadata(&self) -> CacheMetadata; } pub type SharedCache<'a> = Rc>; #[derive(Clone,Copy)] pub struct CacheLocation { pub tag: u32, pub index: u32, pub offset: u32, pub way: u32, } #[derive(Clone)] struct FetchRequest { address: isa::Address, prefetch: bool, // is this a prefetch cycles_left: u32, location: CacheLocation, data: Vec, // hold data temporarily while we wait for an entire line error: Option, // in case next level returns an error waiting_on: u32, // which word of the block are we waiting on } #[derive(Clone)] struct CacheBlock { valid: bool, tag: u32, contents: Vec, fetch_request: Option, } pub trait CacheEventHandler { fn block_fetched(&self, location: CacheLocation); } pub struct EmptyCacheEventHandler {} impl CacheEventHandler for EmptyCacheEventHandler { fn block_fetched(&self, _: CacheLocation) {} } // TODO: probably want different caches for different strategies, and // investigate how LRU is implemented // TODO: use hashtable for a way? // TODO: hashtable-based FA cache? pub struct DirectMappedCache<'a, T: CacheEventHandler> { num_sets: u32, block_words: u32, cache: Vec, next_level: SharedMemory<'a>, events: T, } impl<'a, T: CacheEventHandler> DirectMappedCache<'a, T> { pub fn new(sets: u32, block_words: u32, next_level: SharedMemory<'a>, events: T) -> DirectMappedCache<'a, T> { let set = CacheBlock { valid: false, tag: 0, contents: vec![isa::Word(0); block_words as usize], fetch_request: None, }; DirectMappedCache { num_sets: sets, block_words: block_words, cache: vec![set; sets as usize], next_level: next_level, events: events, } } pub fn parse_address(&self, address: isa::Address) -> (u32, u32, u32) { // TODO: use constant in ISA module for word->byte conversion let offset_mask = (self.block_words * 4 - 1) as u32; let offset = address & offset_mask; let index_mask = (self.num_sets - 1) as u32; let index_shift = 32 - (self.block_words * 4).leading_zeros() - 1; let index = (address >> index_shift) & index_mask; let tag_shift = index_shift + (32 - self.num_sets.leading_zeros()) - 1; let tag = address >> tag_shift; (tag.0, index.0, offset.0) } fn normalize_address(&self, address: isa::Address) -> isa::Address { let offset_mask = !(self.block_words * 4 - 1); address & offset_mask } } impl<'a, T: CacheEventHandler> MemoryInterface for DirectMappedCache<'a, T> { fn latency(&self) -> u32 { 0 } fn step(&mut self) { for set in self.cache.iter_mut() { if let Some(ref mut fetch_request) = set.fetch_request { // Start filling the cache once the cycles_left would // have hit 0, so that the consumer never gets // stall_cycles = 0 if fetch_request.cycles_left > 1 { fetch_request.cycles_left -= 1; return; } // read all the words in a line from the next // level, until we get a stall for offset in fetch_request.waiting_on..self.block_words { let result = self.next_level .borrow_mut() .read_word(fetch_request.address + (4 * offset)); match result { Ok(data) => { fetch_request.data[offset as usize] = data; fetch_request.waiting_on += 1; }, Err(MemoryError::CacheMiss { stall_cycles, .. }) => { fetch_request.cycles_left = stall_cycles; continue; }, Err(MemoryError::InvalidAddress) => { fetch_request.error = Some(MemoryError::InvalidAddress); continue; } } } // All words fetched, write to cache self.events.block_fetched(fetch_request.location); set.tag = fetch_request.location.tag; set.contents = fetch_request.data.clone(); set.valid = true; } set.fetch_request = None; } } fn is_address_accessible(&self, address: isa::Address) -> bool { let (tag, index, _) = self.parse_address(address); let ref set = self.cache[index as usize]; set.valid && set.tag == tag } fn read_word(&mut self, address: isa::Address) -> Result { let normalized = self.normalize_address(address); let stall = self.next_level.borrow().latency(); let (tag, index, offset) = self.parse_address(address); let location = CacheLocation { tag: tag, index: index, offset: offset, way: 0, }; let ref mut set = self.cache[index as usize]; if set.valid && set.tag == tag { return Ok(set.contents[(offset / 4) as usize]); } else if let None = set.fetch_request { set.fetch_request = Some(FetchRequest { address: normalized, prefetch: false, cycles_left: stall, location: location, data: vec![isa::Word(0); self.block_words as usize], error: None, waiting_on: 0, }); } else if let Some(ref mut fetch_request) = set.fetch_request { if let Some(ref err) = fetch_request.error { if fetch_request.address == normalized { return Err(err.clone()); } else { fetch_request.address = normalized; fetch_request.prefetch = false; fetch_request.cycles_left = stall; fetch_request.location = location; fetch_request.waiting_on = 0; } } // Do the assignment outside the borrow of the error fetch_request.error = None; return Err(MemoryError::CacheMiss { stall_cycles: fetch_request.cycles_left, retry: true, }); } Err(MemoryError::CacheMiss { stall_cycles: stall, retry: true, }) } fn write_word(&mut self, address: isa::Address, value: isa::Word) -> Result<()> { // Write-allocate policy match self.read_word(address) { Ok(_) => { let (tag, index, offset) = self.parse_address(address); let ref mut set = self.cache[index as usize]; if set.valid && set.tag == tag { set.contents[(offset / 4) as usize] = value; // Write-through policy let result = self.next_level.borrow_mut() .write_word(address, value); match result { Ok(()) => Ok(()), Err(e) => Err(e), } } else { panic!("Could not find supposedly read word"); } }, Err(e) => Err(e), } } } impl<'a, T: CacheEventHandler> CacheInterface for DirectMappedCache<'a, T> { fn cache_metadata(&self) -> CacheMetadata { let tags = { let mut tags = Vec::new(); for set in self.cache.iter() { if set.valid { tags.push(Some(isa::Word(set.tag))); } else { tags.push(None); } } tags }; CacheMetadata { num_sets: self.num_sets as usize, num_ways: 1, num_block_words: self.block_words as usize, tags: tags, } } }