// 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::{self, Instruction, IsaType}; #[derive(Clone, Debug, PartialEq)] pub enum MemoryError { InvalidAddress, CacheMiss { /// How many cycles to stall stall_cycles: u32, /// Whether the load or store should be retried retry: bool, }, } pub type Result = ::std::result::Result; pub trait MemoryInterface { fn latency(&self) -> u32; fn step(&mut self); // fn prefetch(&mut self, address: isa::Address); // fn invalidate(&mut self, address: isa::Address); fn is_address_accessible(&self, address: isa::Address) -> bool; fn read_word(&mut self, address: isa::Address) -> Result; fn write_word(&mut self, address: isa::Address, value: isa::Word) -> Result<()>; fn read_instruction(&mut self, address: isa::Address) -> Option { match self.read_word(address) { Ok(word) => Some(Instruction::new(word)), Err(_) => None, } } // TODO: check address more thoroughly fn read_halfword(&mut self, address: isa::Address) -> Result { let result = self.read_word(address); let offset = (address & 0b10).0; match result { Ok(word) => match offset { 0 => Ok((word & 0xFFFF).as_half_word()), 2 => Ok(((word & 0xFFFF0000) >> 16).as_half_word()), _ => panic!("Invalid halfword offset: address {:x}", address), }, Err(e) => Err(e), } } fn write_halfword(&mut self, address: isa::Address, value: isa::HalfWord) -> Result<()> { let result = self.read_word(address); let offset = (address & 0b10).0; let value = value.as_word(); match result { Ok(word) => { let value = match offset { 0 => (word & 0xFFFF0000) | value, 2 => (word & 0x0000FFFF) | (value << 16), _ => panic!("Invalid halfword offset: address {:x}", address), }; self.write_word(address, value) }, Err(e) => Err(e), } } fn read_byte(&mut self, address: isa::Address) -> Result { let result = self.read_word(address); let offset = (address % 4).0; match result { Ok(word) => match offset { 0 => Ok((word & 0xFF).as_byte()), 1 => Ok(((word & 0xFF00) >> 8).as_byte()), 2 => Ok(((word & 0xFF0000) >> 16).as_byte()), 3 => Ok(((word & 0xFF000000) >> 24).as_byte()), _ => panic!("Invalid byte offset: {:x}", address), }, Err(e) => Err(e), } } fn write_byte(&mut self, address: isa::Address, value: isa::Byte) -> Result<()> { let result = self.read_word(address); let offset = (address % 4).0; let value = value.as_word(); match result { Ok(word) => { let value = match offset { 0 => (word & !(0xFF)) | value, 1 => (word & !(0xFF00)) | (value << 8), 2 => (word & !(0xFF0000)) | (value << 16), 3 => (word & !(0xFF000000)) | (value << 24), _ => panic!("Invalid byte offset: {:x}", address), }; self.write_word(address, value) }, Err(e) => Err(e), } } } pub type SharedMemory<'a> = Rc>; pub trait Mmu { fn translate(&self, address: isa::Address) -> isa::Address; } pub struct IdentityMmu {} pub struct ReverseMmu { top: isa::Address, } impl IdentityMmu { pub fn new() -> IdentityMmu { IdentityMmu {} } } impl Mmu for IdentityMmu { fn translate(&self, address: isa::Address) -> isa::Address { address } } impl ReverseMmu { pub fn new(top: isa::Address) -> ReverseMmu { ReverseMmu { top: top, } } } impl Mmu for ReverseMmu { fn translate(&self, address: isa::Address) -> isa::Address { let offset = address % 4; (self.top - 4 - (address - offset)) + offset } } pub struct Memory { memory: Vec, } fn copy_u8_into_u32(mmu: &T, base: usize, src: &[u8], dst: &mut [u32]) { for (offset, word) in src.chunks(4).enumerate() { let word = if word.len() == 4 { (word[0] as u32) | ((word[1] as u32) << 8) | ((word[2] as u32) << 16) | ((word[3] as u32) << 24) } else if word.len() == 3 { (word[0] as u32) | ((word[1] as u32) << 8) | ((word[2] as u32) << 16) } else if word.len() == 2 { (word[0] as u32) | ((word[1] as u32) << 8) } else { word[0] as u32 }; let addr = isa::Word((base as u32) + ((4 * offset) as u32)); let addr = mmu.translate(addr) / 4; dst[addr.0 as usize] = word; } } impl Memory { pub fn new(size: usize) -> Memory { Memory { memory: vec![0; size as usize], } } pub fn write_segment(&mut self, mmu: &T, data: &[u8], offset: usize) { copy_u8_into_u32(mmu, offset, data, &mut self.memory); } } impl MemoryInterface for Memory { fn latency(&self) -> u32 { 100 } fn step(&mut self) {} fn is_address_accessible(&self, address: isa::Address) -> bool { ((address / 4).0 as usize) < self.memory.len() } fn read_word(&mut self, address: isa::Address) -> Result { // memory is word-addressed but addresses are byte-addressed self.memory.get((address / 4).0 as usize) .map(Clone::clone) .map(isa::Word) .ok_or(MemoryError::InvalidAddress) } fn write_word(&mut self, address: isa::Address, value: isa::Word) -> Result<()> { let address = (address / 4).0 as usize; if address >= self.memory.len() || address <= 0 { Err(MemoryError::InvalidAddress) } else { self.memory[address] = value.0; Ok(()) } } fn read_instruction(&mut self, pc: isa::Address) -> Option { self.memory.get((pc / 4).0 as usize) .map(Clone::clone) .map(isa::Word) .map(Instruction::new) } }