diff options
-rw-r--r-- | src/globals.rs | 8 | ||||
-rw-r--r-- | src/main.rs | 36 | ||||
-rw-r--r-- | src/memory_tracker.rs | 19 | ||||
-rw-r--r-- | src/shareable_cache.rs | 233 | ||||
-rw-r--r-- | src/system.rs | 6 |
5 files changed, 175 insertions, 127 deletions
diff --git a/src/globals.rs b/src/globals.rs index 27d3bbd..2d2a067 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -1,12 +1,12 @@ use std::cell::RefCell; use std::rc::Rc; -use rustv::isa; +use rustv::isa::{self, IsaType}; use rustv::memory::{Memory, MemoryInterface, Mmu}; use memory_tracker::MemoryTracker; -pub const HOME_STATUS: isa::Address = 0xFFF00; +pub const HOME_STATUS: isa::Address = isa::Word(0xFFF00); /// Update the various global data structures as requested by other /// subsystems. @@ -35,10 +35,10 @@ impl<'a> GlobalsUpdater<'a> { let address2 = self.mmu2.translate(HOME_STATUS + 28 + offset); let _ = self.memory.borrow_mut().write_word( address1, - (-1 as i32) as isa::Word); + isa::SignedWord(-1).as_word()); let _ = self.memory.borrow_mut().write_word( address2, - (-1 as i32) as isa::Word); + isa::SignedWord(-1).as_word()); } } diff --git a/src/main.rs b/src/main.rs index 615a965..ce1f336 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(augmented_assignments)] + extern crate docopt; #[macro_use] extern crate log; extern crate env_logger; @@ -19,7 +21,7 @@ use std::cell::RefCell; use docopt::Docopt; use rustv::elfloader; -use rustv::isa; +use rustv::isa::{self, IsaType}; use rustv::memory; use rustv::memory::Mmu; use rustv::simulator; @@ -53,7 +55,7 @@ fn load_program<T: memory::Mmu>(memory: &mut memory::Memory, f.read_to_end(&mut buffer).unwrap(); let elf = elfloader::ElfBinary::new("test", &buffer).unwrap(); - let start = elf.file_header().entry as isa::Address; + let start = isa::Word(elf.file_header().entry); for p in elf.section_headers() { if p.flags.0 & elfloader::elf::SHF_ALLOC.0 != 0 { @@ -64,6 +66,16 @@ fn load_program<T: memory::Mmu>(memory: &mut memory::Memory, start } +fn generate_payload() -> isa::Byte { + let mut payload = rand::random::<u8>(); + while payload == shareable_cache::WRITE_TRAP_VALUE.0 || + payload == shareable_cache::AREA_TRAP_VALUE.0 { + payload = rand::random::<u8>(); + } + + isa::Byte(payload) +} + fn main() { env_logger::init().unwrap(); @@ -73,14 +85,14 @@ fn main() { let mmu = memory::IdentityMmu::new(); // TODO: account for word-vs-byte addressed. Use newtype pattern? - let mmu2 = memory::ReverseMmu::new(0x4000000); + let mmu2 = memory::ReverseMmu::new(isa::Word(0x4000000)); let mut memory = memory::Memory::new(0x1000000); let start1 = load_program(&mut memory, &mmu, &args.arg_program1); let start2 = load_program(&mut memory, &mmu2, &args.arg_program2); - let payload1 = rand::random::<u8>(); - let payload2 = rand::random::<u8>(); + let payload1 = generate_payload(); + let payload2 = generate_payload(); let memory = MemoryTracker::new(memory, payload1, payload2); // TODO: initialize global data @@ -99,11 +111,11 @@ fn main() { ShareableCache::new(i, cache_ref.clone(), cache2_ref.clone()))); core_caches.push(core_cache.clone()); let mut core = simulator::Core::new( - i, start1, (0x100000 * (i + 1)) as u32, core_cache, + i, start1, isa::Word(0x100000 * (i + 1) as u32), core_cache, Box::new(memory::IdentityMmu::new()) ); - core.registers().write_word(isa::Register::X10, i as isa::Word); - core.registers().write_word(isa::Register::X11, payload1 as isa::Word); + core.registers().write_word(isa::Register::X10, isa::Word(i as u32)); + core.registers().write_word(isa::Register::X11, payload1.as_word()); cores.push(core); } @@ -112,11 +124,11 @@ fn main() { ShareableCache::new(i, cache2_ref.clone(), cache_ref.clone()))); core_caches.push(core_cache.clone()); let mut core = simulator::Core::new( - i, start2, (0x100000 * (i + 1)) as u32, core_cache, - Box::new(memory::ReverseMmu::new(0x4000000)) + i, start2, isa::Word(0x100000 * (i + 1) as u32), core_cache, + Box::new(memory::ReverseMmu::new(isa::Word(0x4000000))) ); - core.registers().write_word(isa::Register::X10, (i - 4) as isa::Word); - core.registers().write_word(isa::Register::X11, payload2 as isa::Word); + core.registers().write_word(isa::Register::X10, isa::Word((i - 4) as u32)); + core.registers().write_word(isa::Register::X11, payload2.as_word()); cores.push(core); } diff --git a/src/memory_tracker.rs b/src/memory_tracker.rs index e35bf2d..26c986a 100644 --- a/src/memory_tracker.rs +++ b/src/memory_tracker.rs @@ -1,16 +1,17 @@ -use rustv::isa; +use rustv::isa::{self, IsaType}; use rustv::memory::{Memory, MemoryInterface, Result}; pub struct MemoryTracker { memory: Memory, - program1_byte: u8, - program2_byte: u8, - program1: i64, - program2: i64, + program1_byte: isa::Byte, + program2_byte: isa::Byte, + program1: i32, + program2: i32, } impl MemoryTracker { - pub fn new(memory: Memory, program1: u8, program2: u8) -> MemoryTracker { + pub fn new(memory: Memory, + program1: isa::Byte, program2: isa::Byte) -> MemoryTracker { MemoryTracker { memory: memory, program1_byte: program1, @@ -20,7 +21,7 @@ impl MemoryTracker { } } - pub fn score(&self) -> (i64, i64) { + pub fn score(&self) -> (i32, i32) { (self.program1, self.program2) } } @@ -48,7 +49,7 @@ impl MemoryInterface for MemoryTracker { if let Ok(original) = original { if let Ok(()) = result { - let p1b = self.program1_byte as u32; + let p1b = self.program1_byte.as_word(); let mut p1orig = 0; let mut p1new = 0; if original & 0xFF == p1b { p1orig += 1; } @@ -63,7 +64,7 @@ impl MemoryInterface for MemoryTracker { self.program1 += p1new - p1orig; - let p2b = self.program2_byte as u32; + let p2b = self.program2_byte.as_word(); let mut p2orig = 0; let mut p2new = 0; if original & 0xFF == p2b { p2orig += 1; } diff --git a/src/shareable_cache.rs b/src/shareable_cache.rs index 4da9777..a4c993b 100644 --- a/src/shareable_cache.rs +++ b/src/shareable_cache.rs @@ -1,8 +1,10 @@ -use rustv::isa; +use std::collections::HashSet; + +use rustv::isa::{self, IsaType}; use rustv::memory::{MemoryError, MemoryInterface, Result, SharedMemory}; -pub const WRITE_TRAP_VALUE: u8 = 0x42; -pub const AREA_TRAP_VALUE: u8 = 0x43; +pub const WRITE_TRAP_VALUE: isa::Byte = isa::Byte(0xE0); +pub const AREA_TRAP_VALUE: isa::Byte = isa::Byte(0xE4); pub const WRITE_TRAP_STALL: u32 = 1_000_000; @@ -14,6 +16,126 @@ pub struct ShareableCache<'a> { secondary: SharedMemory<'a>, secondary_enabled: bool, use_secondary: bool, + traps_hit: HashSet<isa::Address>, +} + +// Cache snooping: update all cache lines when a write is made +macro_rules! snoop { + ($cache: expr, $write_value: ident, $address: ident, $value: ident) => { + if $cache.borrow().is_address_accessible($address) { + // depends on invariant: write_word completes instantly + // when the address is accessible (in-cache) + let _ = $cache.borrow_mut().$write_value($address, $value); + } + } +} + +macro_rules! check_traps { + ($core_id: expr, $cache: expr, $traps_hit: expr, $write_value: ident, + $address: ident, $value: ident) => {{ + // Requires invariant: if x is the address of a word, x + 0 is + // the address of the LSB and x + 3 is the address of the MSB. + + let accessible = { + $cache.borrow().is_address_accessible($address) + }; + + if accessible { + // No stall - check for trap + let old_value = { + $cache.borrow_mut().read_word($address) + }; + + match old_value { + Ok(old_value) => { + let old_bytes = old_value.as_bytes(); + let new_bytes = $value.as_bytes(); + + let mut num_traps_hit = 0; + + let iter = old_bytes.iter() + .take(new_bytes.len()) + .enumerate(); + for (offset, old) in iter { + // Make sure offset bits are cleared before + // adding offset + let trap_address = ($address & 0xFFFFFFFC) + + isa::Word(offset as u32); + if *old == WRITE_TRAP_VALUE && + !$traps_hit.contains(&trap_address) { + num_traps_hit += 1; + $traps_hit.insert(trap_address); + } + else if *old != WRITE_TRAP_VALUE { + $traps_hit.remove(&trap_address); + } + } + + if num_traps_hit > 0 { + info!("[memory] core {}: {} write trap(s) hit at address {:x},\ + stalling for 1_000_000 cycles each", + $core_id, num_traps_hit, $address); + Err(MemoryError::CacheMiss { + stall_cycles: num_traps_hit * WRITE_TRAP_STALL, + }) + } + else { + $cache.borrow_mut().$write_value($address, $value) + } + } + Err(e) => { + panic!("Could not read accessible value: {:?}", e) + } + } + } + else { + // Not in cache - defer to fetch + $cache.borrow_mut().$write_value($address, $value) + } + }} +} + +macro_rules! write_value { + ($self_: ident, $write_value: ident, $address: ident, $value: ident) => { + if $self_.secondary_enabled { + let (primary_accessible, secondary_accessible) = + $self_.address_accessible($address); + + if primary_accessible { + snoop!($self_.secondary, $write_value, $address, $value); + check_traps!($self_.core_id, $self_.primary, + &mut $self_.traps_hit, + $write_value, $address, $value) + } + else if secondary_accessible { + snoop!($self_.primary, $write_value, $address, $value); + check_traps!($self_.core_id, $self_.secondary, + &mut $self_.traps_hit, + $write_value, $address, $value) + } + else { + $self_.use_secondary = !$self_.use_secondary; + if $self_.use_secondary { + snoop!($self_.primary, $write_value, $address, $value); + check_traps!($self_.core_id, $self_.secondary, + &mut $self_.traps_hit, + $write_value, $address, $value) + } + else { + snoop!($self_.secondary, $write_value, $address, $value); + check_traps!($self_.core_id, $self_.primary, + &mut $self_.traps_hit, + $write_value, $address, $value) + } + } + } + else { + snoop!($self_.secondary, $write_value, $address, $value); + check_traps!($self_.core_id, $self_.primary, + &mut $self_.traps_hit, + $write_value, $address, $value) + } + } } impl<'a> ShareableCache<'a> { @@ -26,6 +148,7 @@ impl<'a> ShareableCache<'a> { secondary: cache2.clone(), secondary_enabled: false, use_secondary: false, + traps_hit: HashSet::new(), } } @@ -52,75 +175,6 @@ impl<'a> ShareableCache<'a> { } } -fn snoop(cache: &mut SharedMemory<'a>, - address: isa::Address, value: isa::Word) { - // Cache snooping: update all cache lines when a write is made - if cache.borrow().is_address_accessible(address) { - // Depends on invariant: write_word completes instantly - // when the address is accessible (in-cache) - let _ = cache.borrow_mut().write_word(address, value); - } -} - -fn as_bytes(word: isa::Word) -> [isa::Byte; 4] { - [(word & 0xFF) as isa::Byte, - ((word >> 8) & 0xFF) as isa::Byte, - ((word >> 16) & 0xFF) as isa::Byte, - ((word >> 24) & 0xFF) as isa::Byte] -} - -fn check_traps(cache: &mut MemoryInterface, - address: isa::Address, value: isa::Word) -> Result<()> { - if cache.is_address_accessible(address) { - // No stall - check for trap - let old_value = cache.read_word(address); - - match old_value { - Ok(old_value) => { - // TODO: helper method on isa::Word (would require - // newtype) for accessing individual bytes? - - let old_bytes = as_bytes(old_value); - let new_bytes = as_bytes(value); - - let mut traps_set = 0; - let mut traps_hit = 0; - - for (old, new) in old_bytes.iter().zip(new_bytes.iter()) { - if *old == WRITE_TRAP_VALUE && - *new != WRITE_TRAP_VALUE { - traps_hit += 1; - } - else if *old != WRITE_TRAP_VALUE && - *new == WRITE_TRAP_VALUE { - traps_set += 1; - } - } - - if traps_hit > 0 { - info!("[memory] {} write trap(s) hit at address {:x},\ - stalling for 1_000_000 cycles each", - traps_hit, address); - Err(MemoryError::CacheMiss { - stall_cycles: traps_hit * WRITE_TRAP_STALL, - }) - } - else { - cache.cache.write_word(address, value) - } - } - Err(e) => { - panic!("Could not read accessible value: {:?}", e) - } - } - } - else { - // Not in cache - defer to fetch - cache.cache.write_word(address, value) - } -} - - impl<'a> MemoryInterface for ShareableCache<'a> { fn latency(&self) -> u32 { self.primary.borrow().latency() @@ -171,33 +225,14 @@ impl<'a> MemoryInterface for ShareableCache<'a> { } fn write_word(&mut self, address: isa::Address, value: isa::Word) -> Result<()> { - if self.secondary_enabled { - let (primary_accessible, secondary_accessible) = - self.address_accessible(address); + write_value!(self, write_word, address, value) + } - if primary_accessible { - snoop(&mut self.secondary, address, value); - check_traps(&self.primary.borrow_mut(), address, value) - } - else if secondary_accessible { - snoop(&mut self.primary, address, value); - check_traps(&self.secondary.borrow_mut(), address, value) - } - else { - self.use_secondary = !self.use_secondary; - if self.use_secondary { - snoop(&mut self.primary, address, value); - check_traps(&self.secondary.borrow_mut(), address, value) - } - else { - snoop(&mut self.secondary, address, value); - check_traps(&self.primary.borrow_mut(), address, value) - } - } - } - else { - snoop(&mut self.secondary, address, value); - check_traps(&self.primary.borrow_mut(), address, value) - } + fn write_halfword(&mut self, address: isa::Address, value: isa::HalfWord) -> Result<()> { + write_value!(self, write_halfword, address, value) + } + + fn write_byte(&mut self, address: isa::Address, value: isa::Byte) -> Result<()> { + write_value!(self, write_byte, address, value) } } diff --git a/src/system.rs b/src/system.rs index c0343bb..a69beb4 100644 --- a/src/system.rs +++ b/src/system.rs @@ -34,11 +34,11 @@ impl<'a> SyscallHandler<'a> { loop { let c = self.memory.borrow_mut().read_byte(base); - if let Ok(0x00) = c { + if let Ok(isa::Byte(0x00)) = c { break; } else if let Ok(c) = c { - string.push(c); + string.push(c.0); } base += 1; @@ -81,7 +81,7 @@ impl<'a> syscall::SyscallHandler for SyscallHandler<'a> { registers: &mut RegisterFile, mmu: &Mmu) -> Option<Trap> { let syscall_number = registers.read_word(isa::Register::X10); - match syscall_number { + match syscall_number.0 { 22 => self.prints(registers), 23 => self.printi(registers), 24 => self.enable_secondary(core_id), |