aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Li <li.davidm96@gmail.com>2016-01-08 10:04:27 -0700
committerDavid Li <li.davidm96@gmail.com>2016-01-08 10:04:27 -0700
commitad805c698cd1c4d68d090f842fb14621c32ee8d6 (patch)
treefd357647c13c461370d34a067b84cc7aac94c7d3
parent940a5ae77a1efc8f89fe5b56fe4a4ae34a0e480d (diff)
Refactor into multiple files
-rw-r--r--Cargo.lock30
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs141
-rw-r--r--src/memory_tracker.rs85
-rw-r--r--src/shareable_cache.rs104
-rw-r--r--src/system.rs67
6 files changed, 314 insertions, 114 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 12379b0..b3f0c3a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5,6 +5,7 @@ dependencies = [
"docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rustv 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -31,6 +32,15 @@ version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "kernel32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "libc"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -76,3 +86,23 @@ name = "strsim"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "time"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
diff --git a/Cargo.toml b/Cargo.toml
index 7efe290..6515d90 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,3 +7,4 @@ authors = ["David Li <li.davidm96@gmail.com>"]
rustv = "0.3.0"
docopt = "0.6"
rustc-serialize = "0.3"
+time = "0.1"
diff --git a/src/main.rs b/src/main.rs
index 722efdb..50cc8ee 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,11 @@
extern crate docopt;
extern crate rustc_serialize;
extern crate rustv;
+extern crate time;
+
+mod memory_tracker;
+mod shareable_cache;
+mod system;
use std::fs::File;
use std::io::Read;
@@ -12,12 +17,11 @@ use docopt::Docopt;
use rustv::elfloader;
use rustv::isa;
use rustv::memory;
-use rustv::memory::MemoryInterface;
use rustv::memory::Mmu;
-use rustv::register_file;
use rustv::simulator;
-use rustv::syscall;
-use rustv::trap;
+
+use memory_tracker::MemoryTracker;
+use system::SyscallHandler;
const USAGE: &'static str = "
cacheracer - A F/OSS implementation of the CS 3410 CacheRacer
@@ -36,102 +40,7 @@ struct Args {
arg_program2: String,
}
-struct MemoryTracker {
- memory: memory::Memory,
- program1_byte: u8,
- program2_byte: u8,
- program1: usize,
- program2: usize,
-}
-
-impl memory::MemoryInterface for MemoryTracker {
- fn latency(&self) -> u32 {
- self.memory.latency()
- }
-
- fn step(&mut self) {
- self.memory.step();
- }
-
- fn read_word(&mut self, address: isa::Address) -> memory::Result<isa::Word> {
- self.memory.read_word(address)
- }
-
- fn write_word(&mut self, address: isa::Address, value: isa::Word) -> memory::Result<()> {
- let original = self.memory.read_word(address);
- let result = self.memory.write_word(address, value);
-
- if let Ok(original) = original {
- if let Ok(()) = result {
- let p1b = self.program1_byte as u32;
- let mut p1orig = 0;
- let mut p1new = 0;
- if original & 0xFF == p1b { p1orig += 1; }
- if (original >> 8) & 0xFF == p1b { p1orig += 1; }
- if (original >> 8) & 0xFF == p1b { p1orig += 1; }
- if (original >> 8) & 0xFF == p1b { p1orig += 1; }
-
- if value & 0xFF == p1b { p1new += 1; }
- if (value >> 8) & 0xFF == p1b { p1new += 1; }
- if (value >> 8) & 0xFF == p1b { p1new += 1; }
- if (value >> 8) & 0xFF == p1b { p1new += 1; }
-
- self.program1 += p1new - p1orig;
-
- let p2b = self.program2_byte as u32;
- let mut p2orig = 0;
- let mut p2new = 0;
- if original & 0xFF == p2b { p2orig += 1; }
- if (original >> 8) & 0xFF == p2b { p2orig += 1; }
- if (original >> 8) & 0xFF == p2b { p2orig += 1; }
- if (original >> 8) & 0xFF == p2b { p2orig += 1; }
-
- if value & 0xFF == p2b { p2new += 1; }
- if (value >> 8) & 0xFF == p2b { p2new += 1; }
- if (value >> 8) & 0xFF == p2b { p2new += 1; }
- if (value >> 8) & 0xFF == p2b { p2new += 1; }
-
- self.program2 += p2new - p2orig;
- }
- }
-
- result
- }
-}
-
-struct SyscallHandler<'a> {
- memory: memory::SharedMemory<'a>,
-}
-
-impl<'a> syscall::SyscallHandler for SyscallHandler<'a> {
- fn syscall(&mut self, registers: &mut register_file::RegisterFile) -> Option<trap::Trap> {
- println!("Syscall number {}", registers.read_word(isa::Register::X10));
- let mut base = registers.read_word(isa::Register::X11);
- let mut string = vec![];
- loop {
- let c = self.memory.borrow_mut().read_byte(base);
-
- if let Ok(0x00) = c {
- break;
- }
- else if let Ok(c) = c {
- string.push(c);
- }
-
- base += 1;
- }
-
- let result = std::str::from_utf8(&string);
- if let Ok(string) = result {
- println!("{}", string);
- }
- else {
- println!("Error printing string: {:?}", result);
- }
- None
- }
-}
fn load_program<T: memory::Mmu>(memory: &mut memory::Memory,
mmu: &T, path: &str) -> isa::Address {
@@ -159,36 +68,40 @@ fn main() {
.unwrap_or_else(|e| e.exit());
let mmu = memory::IdentityMmu::new();
- let mmu2 = memory::ReverseMmu::new(0x8000);
- let mut memory = memory::Memory::new(0x10000);
+ // TODO: account for word-vs-byte addressed. Use newtype pattern?
+ let mmu2 = memory::ReverseMmu::new(0x4000000);
+ let mut memory = memory::Memory::new(0x400000);
let start1 = load_program(&mut memory, &mmu, &args.arg_program1);
let start2 = load_program(&mut memory, &mmu2, &args.arg_program2);
- let mut memory = MemoryTracker {
- memory: memory,
- program1_byte: 0x43,
- program2_byte: 0x42,
- program1: 0,
- program2: 0,
- };
+ let memory = MemoryTracker::new(memory, 0x43, 0x42);
let memory_ref = Rc::new(RefCell::new(memory));
- let cache = memory::DirectMappedCache::new(4, 4, memory_ref.clone());
+ let cache = memory::DirectMappedCache::new(4, 64, memory_ref.clone());
let cache_ref = Rc::new(RefCell::new(cache));
+ let cache2 = memory::DirectMappedCache::new(4, 64, memory_ref.clone());
+ let cache2_ref = Rc::new(RefCell::new(cache2));
+
let core = simulator::Core::new(
start1, 0x1000,
cache_ref.clone(), Box::new(mmu));
let core2 = simulator::Core::new(
start2, 0x1000,
- cache_ref.clone(), Box::new(mmu2));
+ cache2_ref.clone(), Box::new(mmu2));
let cores = vec![core, core2];
- let system = SyscallHandler { memory: memory_ref.clone(), };
+ let system = SyscallHandler::new(memory_ref.clone());
let mut simulator = simulator::Simulator::new(
cores, memory_ref.clone(), system);
- simulator.run();
+ // let cycles = 0x1000000;
+ let cycles = 0x1000000;
+ let start = time::precise_time_s();
+ simulator.run_max(cycles);
+ let end = time::precise_time_s();
- println!("Program 1 bytes written: {}", memory_ref.borrow().program1);
- println!("Program 2 bytes written: {}", memory_ref.borrow().program2);
+ println!("{} cycles in {:04} seconds ({} cycles/sec)", cycles, end - start, (cycles as f64) / (end - start));
+ let (p1, p2) = memory_ref.borrow().score();
+ println!("Program 1 bytes written: {}", p1);
+ println!("Program 2 bytes written: {}", p2);
}
diff --git a/src/memory_tracker.rs b/src/memory_tracker.rs
new file mode 100644
index 0000000..e35bf2d
--- /dev/null
+++ b/src/memory_tracker.rs
@@ -0,0 +1,85 @@
+use rustv::isa;
+use rustv::memory::{Memory, MemoryInterface, Result};
+
+pub struct MemoryTracker {
+ memory: Memory,
+ program1_byte: u8,
+ program2_byte: u8,
+ program1: i64,
+ program2: i64,
+}
+
+impl MemoryTracker {
+ pub fn new(memory: Memory, program1: u8, program2: u8) -> MemoryTracker {
+ MemoryTracker {
+ memory: memory,
+ program1_byte: program1,
+ program2_byte: program2,
+ program1: 0,
+ program2: 0,
+ }
+ }
+
+ pub fn score(&self) -> (i64, i64) {
+ (self.program1, self.program2)
+ }
+}
+
+impl MemoryInterface for MemoryTracker {
+ fn latency(&self) -> u32 {
+ self.memory.latency()
+ }
+
+ fn step(&mut self) {
+ self.memory.step();
+ }
+
+ fn is_address_accessible(&self, address: isa::Address) -> bool {
+ self.memory.is_address_accessible(address)
+ }
+
+ fn read_word(&mut self, address: isa::Address) -> Result<isa::Word> {
+ self.memory.read_word(address)
+ }
+
+ fn write_word(&mut self, address: isa::Address, value: isa::Word) -> Result<()> {
+ let original = self.memory.read_word(address);
+ let result = self.memory.write_word(address, value);
+
+ if let Ok(original) = original {
+ if let Ok(()) = result {
+ let p1b = self.program1_byte as u32;
+ let mut p1orig = 0;
+ let mut p1new = 0;
+ if original & 0xFF == p1b { p1orig += 1; }
+ if (original >> 8) & 0xFF == p1b { p1orig += 1; }
+ if (original >> 16) & 0xFF == p1b { p1orig += 1; }
+ if (original >> 24) & 0xFF == p1b { p1orig += 1; }
+
+ if value & 0xFF == p1b { p1new += 1; }
+ if (value >> 8) & 0xFF == p1b { p1new += 1; }
+ if (value >> 16) & 0xFF == p1b { p1new += 1; }
+ if (value >> 24) & 0xFF == p1b { p1new += 1; }
+
+ self.program1 += p1new - p1orig;
+
+ let p2b = self.program2_byte as u32;
+ let mut p2orig = 0;
+ let mut p2new = 0;
+ if original & 0xFF == p2b { p2orig += 1; }
+ if (original >> 8) & 0xFF == p2b { p2orig += 1; }
+ if (original >> 16) & 0xFF == p2b { p2orig += 1; }
+ if (original >> 24) & 0xFF == p2b { p2orig += 1; }
+
+ if value & 0xFF == p2b { p2new += 1; }
+ if (value >> 8) & 0xFF == p2b { p2new += 1; }
+ if (value >> 16) & 0xFF == p2b { p2new += 1; }
+ if (value >> 24) & 0xFF == p2b { p2new += 1; }
+
+ self.program2 += p2new - p2orig;
+ }
+ }
+
+ result
+ }
+}
diff --git a/src/shareable_cache.rs b/src/shareable_cache.rs
new file mode 100644
index 0000000..b6c6654
--- /dev/null
+++ b/src/shareable_cache.rs
@@ -0,0 +1,104 @@
+use rustv::isa;
+use rustv::memory::{MemoryInterface, Result, SharedMemory};
+
+/// A cache that can be used as two separate caches or one
+/// set-associative cache.
+struct ShareableCache<'a> {
+ // The idea is to create two separate ShareableCaches, one for
+ // each player.
+ primary: SharedMemory<'a>,
+ secondary: SharedMemory<'a>,
+ secondary_enabled: bool,
+ use_secondary: bool,
+}
+
+impl<'a> ShareableCache<'a> {
+ pub fn new(cache1: SharedMemory<'a>, cache2: SharedMemory<'a>)
+ -> (ShareableCache<'a>, ShareableCache<'a>) {
+ (ShareableCache {
+ primary: cache1.clone(),
+ secondary: cache2.clone(),
+ secondary_enabled: false,
+ use_secondary: false,
+ }, ShareableCache {
+ primary: cache2.clone(),
+ secondary: cache1.clone(),
+ secondary_enabled: false,
+ use_secondary: false,
+ })
+ }
+
+ pub fn enable_secondary(&mut self) {
+ self.secondary_enabled = true;
+ self.use_secondary = true;
+ }
+
+ pub fn disable_secondary(&mut self) {
+ self.secondary_enabled = false;
+ }
+}
+
+impl<'a> MemoryInterface for ShareableCache<'a> {
+ fn latency(&self) -> u32 {
+ self.primary.borrow().latency()
+ }
+
+ fn step(&mut self) {
+ // We only step the primary cache. The idea is that the
+ // secondary cache should be the primary cache of another
+ // ShareableCache.
+ self.primary.borrow_mut().step();
+ }
+
+ fn is_address_accessible(&self, address: isa::Address) -> bool {
+ self.primary.borrow().is_address_accessible(address) ||
+ (self.secondary_enabled &&
+ self.secondary.borrow().is_address_accessible(address))
+ }
+
+ fn read_word(&mut self, address: isa::Address) -> Result<isa::Word> {
+ if self.secondary_enabled {
+ if self.primary.borrow().is_address_accessible(address) {
+ self.primary.borrow_mut().read_word(address)
+ }
+ else if self.secondary.borrow().is_address_accessible(address) {
+ self.secondary.borrow_mut().read_word(address)
+ }
+ else {
+ self.use_secondary = !self.use_secondary;
+ if self.use_secondary {
+ self.secondary.borrow_mut().read_word(address)
+ }
+ else {
+ self.primary.borrow_mut().read_word(address)
+ }
+ }
+ }
+ else {
+ self.primary.borrow_mut().read_word(address)
+ }
+ }
+
+ fn write_word(&mut self, address: isa::Address, value: isa::Word) -> Result<()> {
+ if self.secondary_enabled {
+ if self.primary.borrow().is_address_accessible(address) {
+ self.primary.borrow_mut().write_word(address, value)
+ }
+ else if self.secondary.borrow().is_address_accessible(address) {
+ self.secondary.borrow_mut().write_word(address, value)
+ }
+ else {
+ self.use_secondary = !self.use_secondary;
+ if self.use_secondary {
+ self.secondary.borrow_mut().write_word(address, value)
+ }
+ else {
+ self.primary.borrow_mut().write_word(address, value)
+ }
+ }
+ }
+ else {
+ self.primary.borrow_mut().write_word(address, value)
+ }
+ }
+}
diff --git a/src/system.rs b/src/system.rs
new file mode 100644
index 0000000..faadb13
--- /dev/null
+++ b/src/system.rs
@@ -0,0 +1,67 @@
+use std;
+
+use rustv::isa;
+use rustv::memory::SharedMemory;
+use rustv::register_file::RegisterFile;
+use rustv::syscall;
+use rustv::trap::Trap;
+
+pub struct SyscallHandler<'a> {
+ memory: SharedMemory<'a>,
+}
+
+impl<'a> SyscallHandler<'a> {
+ pub fn new(memory: SharedMemory<'a>) -> SyscallHandler<'a> {
+ SyscallHandler {
+ memory: memory
+ }
+ }
+}
+
+impl<'a> SyscallHandler<'a> {
+ fn print(&mut self, registers: &mut RegisterFile) -> Option<Trap> {
+ let mut base = registers.read_word(isa::Register::X11);
+ let mut string = vec![];
+
+ loop {
+ let c = self.memory.borrow_mut().read_byte(base);
+
+ if let Ok(0x00) = c {
+ break;
+ }
+ else if let Ok(c) = c {
+ string.push(c);
+ }
+
+ base += 1;
+ }
+
+ let result = std::str::from_utf8(&string);
+ if let Ok(string) = result {
+ println!("{}", string);
+ }
+ else {
+ println!("Error printing string: not valid UTF-8: {:?}", result);
+ }
+ None
+
+ }
+}
+
+impl<'a> syscall::SyscallHandler for SyscallHandler<'a> {
+ // TODO: is trap really appropriate here?
+ fn syscall(&mut self, registers: &mut RegisterFile) -> Option<Trap> {
+ let syscall_number = registers.read_word(isa::Register::X10);
+
+ match syscall_number {
+ 22 => {
+ self.print(registers)
+ }
+
+ _ => {
+ // TODO: some sort of error reporting
+ None
+ }
+ }
+ }
+}