diff --git a/Cargo.lock b/Cargo.lock index 900eaca..e2a4988 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "modular-bitfield" version = "0.11.2" @@ -157,14 +163,43 @@ dependencies = [ "enum_dispatch", "modular-bitfield", "num", + "strum", ] +[[package]] +name = "rustversion" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6069ca09d878a33f883cc06aaa9718ede171841d3832450354410b718b097232" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.18", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 175c31f..122eeb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ path = "lib/bits" enum_dispatch = "0.3.11" modular-bitfield = "0.11.2" num = "0.4.0" +strum = { version = "0.25.0", features = ["derive"] } diff --git a/src/cpu/mod.rs b/src/cpu/mod.rs index 327650a..5e689cf 100644 --- a/src/cpu/mod.rs +++ b/src/cpu/mod.rs @@ -1,8 +1,8 @@ -use std::{cell::RefCell, rc::Rc}; use crate::system::bus::*; +use std::{cell::RefCell, rc::Rc}; use crate::system::ram; use crate::system::rv32; -use crate::inst; +use crate::ext::decode; // Register ABI Description Saver // x0 zero Zero Immutable @@ -18,19 +18,27 @@ use crate::inst; // x12-x17 a2-a7 Fn args Caller // x18-x27 s2-s11 Saved registers Callee // x28-x31 t3-t6 Temporaries Caller -pub struct CPU { +pub struct CPUState { x: [rv32::Word; 32], pc: rv32::Word, +} + +pub struct CPU { + state: CPUState, bus: Rc>, + instruction_decoder: Rc>, // extensions: } impl CPU { - pub fn new(bus: Rc>) -> CPU { + pub fn new(bus: Rc>, instruction_decoder: Rc>) -> CPU { CPU { - x: [0; 32], - pc: 0, + state: CPUState { + x: [0; 32], + pc: 0, + }, bus, + instruction_decoder, } } @@ -39,75 +47,74 @@ impl CPU { println!("-----------------"); println!("VM > Initializing CPU"); - self.pc = DRAM_BASE as rv32::Word; - self.x[0] = 0x00000000; // x0 is tied to ground - self.x[2] = ram::DRAM_SIZE as u32; // x2 the addressable + self.state.pc = DRAM_BASE as rv32::Word; + self.state.x[0] = 0x00000000; // x0 is tied to ground + self.state.x[2] = ram::DRAM_SIZE as u32; // x2 the addressable } - pub fn get_pc(&mut self) -> rv32::Word { - return self.pc; + pub fn get_pc(&self) -> rv32::Word { + return self.state.pc; } - fn fetch(&mut self) -> inst::Instruction { - inst::Instruction { - inst: self.bus.borrow_mut().load_32(self.pc), - } + fn fetch(&self) -> rv32::Word { + self.bus.borrow_mut().load_32(self.state.pc) } - pub fn exec(&mut self) { - while self.pc - DRAM_BASE < ram::DRAM_SIZE as u32 { + pub fn exec(&mut self) -> Result<(), String> { + while self.state.pc - DRAM_BASE < ram::DRAM_SIZE as u32 { // fetch let inst = self.fetch(); - println!("VM > Fetched 0x{:08x}: 0x{:08x}", self.pc, unsafe { - inst.inst - }); - self.pc = self.pc + rv32::WORD as u32; - self.x[0] = 0x00000000; + println!("VM > Fetched 0x{:08x}: 0x{:08x}", self.state.pc, inst ); + self.state.pc = self.state.pc + rv32::WORD as u32; + self.state.x[0] = 0x00000000; - // decode and execute - // we can use nulltype to extract the opcode - let opcode = unsafe { inst.null.opcode() }; - // then we can match the opcode to extract the op type - match opcode { - inst::I_TYPE => { - let inst = unsafe { inst.I }; - println!("VM > Decoded I Type instruction {:?}", inst); - match inst.funct3() { - 0x0 => { - self.x[inst.rd() as usize] = - self.x[inst.rs1() as usize].wrapping_add(inst.imm() as u32); - } - _ => println!("VM > INST {:03b} not implemented", inst.funct3()), - }; - } - inst::R_TYPE => { - let inst = unsafe { inst.R }; - println!("VM > Decoded R Type instruction {:?}", inst); - match inst.funct3() { - 0x0 => { - self.x[inst.rd() as usize] = self.x[inst.rs1() as usize] - .wrapping_add(self.x[inst.rs2() as usize]); - } - _ => println!("VM > INST {:03b} not implemented", inst.funct3()), - } - } - inst::S_TYPE => { - println!("VM > OPCODE S TYPE not implemented"); - } - _ => println!("VM > OPCODE {:08b} not implemented", opcode), - }; + self.instruction_decoder.borrow_mut().decode_exec_inst(inst, &mut self.state)?; + // // decode and execute + // // we can use nulltype to extract the opcode + // let opcode = unsafe { inst.null.opcode() }; + // // then we can match the opcode to extract the op type + // match opcode { + // inst::I_TYPE => { + // let inst = unsafe { inst.I }; + // println!("VM > Decoded I Type instruction {:?}", inst); + // match inst.funct3() { + // 0x0 => { + // self.x[inst.rd() as usize] = + // self.x[inst.rs1() as usize].wrapping_add(inst.imm() as u32); + // } + // _ => println!("VM > INST {:03b} not implemented", inst.funct3()), + // }; + // } + // inst::R_TYPE => { + // let inst = unsafe { inst.R }; + // println!("VM > Decoded R Type instruction {:?}", inst); + // match inst.funct3() { + // 0x0 => { + // self.x[inst.rd() as usize] = self.x[inst.rs1() as usize] + // .wrapping_add(self.x[inst.rs2() as usize]); + // } + // _ => println!("VM > INST {:03b} not implemented", inst.funct3()), + // } + // } + // inst::S_TYPE => { + // println!("VM > OPCODE S TYPE not implemented"); + // } + // _ => println!("VM > OPCODE {:08b} not implemented", opcode), + // }; + // self.dump_reg(); } + Ok(()) } fn dump_reg(&mut self) { println!("VM > Dumping registers"); - println!("PC : 0x{:08x}", self.pc); + println!("PC : 0x{:08x}", self.state.pc); for i in 0..4 { for j in 0..8 { let coord = (i * 8) + j; - print!("x{0: <2}: {1: <4}", coord, self.x[coord]); + print!("x{0: <2}: {1: <4}", coord, self.state.x[coord]); } println!(""); } diff --git a/src/ext/decode.rs b/src/ext/decode.rs index 971f5cc..01742d6 100644 --- a/src/ext/decode.rs +++ b/src/ext/decode.rs @@ -1,5 +1,6 @@ use bits::match_mask; use enum_dispatch::*; +use strum::{IntoEnumIterator, EnumIter}; use modular_bitfield::prelude::*; use crate::cpu; @@ -75,35 +76,56 @@ pub union GenInstruction { #[enum_dispatch] trait Instruction { + fn name(&self) -> &'static str; fn match_inst(&self, inst: rv32::Word) -> bool; - fn step(&self, inst: rv32::Word, state: &mut cpu::CPU); + fn step(&self, inst: rv32::Word, state: &mut cpu::CPUState); } -#[derive(Copy, Clone)] +#[derive(Default, Copy, Clone)] struct ADDI; impl Instruction for ADDI { + fn name(&self) -> &'static str { + "ADDI" + } + fn match_inst(&self, inst: rv32::Word) -> bool { + println!("VM > Checking ADDI"); + println!("VM > ADDI: 0b{:032b}", inst); + println!("VM > ADDI: 0bxxxxxxxxxxxxxxxxxx000xxxx0010011"); match_mask!(inst, "xxxxxxxxxxxxxxxxxx000xxxx0010011") } - fn step(&self, inst: rv32::Word, state: &mut cpu::CPU) { + fn step(&self, inst: rv32::Word, state: &mut cpu::CPUState) { + println!("VM > Decoded I Type instruction 0x{:08x}", inst); + println!("VM > Executing ADDI"); // self.x[inst.rd() as usize] = self.x[inst.rs1() as usize].wrapping_add(inst.imm() as u32); } } -#[derive(Copy, Clone)] +#[derive(Default, Copy, Clone)] struct ADD; impl Instruction for ADD { + fn name(&self) -> &'static str { + "ADD" + } + fn match_inst(&self, inst: rv32::Word) -> bool { + println!("VM > Checking ADD"); + println!("VM > ADD: 0b{:032b}", inst); + println!("VM > ADD: 0b0000000xxxxxxxxxxx000xxxx0110011"); match_mask!(inst, "0000000xxxxxxxxxxx000xxxx0110011") } - fn step(&self, inst: rv32::Word, state: &mut cpu::CPU) {} + fn step(&self, inst: rv32::Word, state: &mut cpu::CPUState) { + println!("VM > Decoded R Type instruction 0x{:08x}", inst); + println!("VM > Executing ADD"); + } } #[enum_dispatch(Instruction)] +#[derive(EnumIter)] enum ExtensionI { ADDI(ADDI), ADD(ADD), @@ -114,32 +136,37 @@ enum Extensions { } pub struct DecodeCycle { - extensions: Vec, + extensions: Vec, } impl DecodeCycle { - pub fn new(&mut self, ext: Vec) { - for extension in ext { + pub fn new(ext: Vec) -> DecodeCycle { + DecodeCycle { extensions: ext } + } + + pub fn decode_exec_inst(&self, inst: rv32::Word, state: &mut cpu::CPUState) -> Result<(), String> { + // we want to go through each extension and then go through each instruction in that extension + // if we find a match, we want to execute it + // if we don't find a match, we want to return an error + + for extension in self.extensions.iter() { match extension { 'i' => { - self.extensions.push(Extensions::ExtensionI(None)); + println!("VM > Attempting to decode instruction as I extension: 0x{:08x}", inst); + for instruction in ExtensionI::iter() { + println!("VM > Checking instruction: {:?}", instruction.name()); + if instruction.match_inst(inst) { + println!("VM > Decoded instruction as I extension: 0x{:08x}", inst); + instruction.step(inst, state); + return Ok(()); + } + } } _ => { - println!("VM > Unknown Extension '{}'", extension); + println!("VM > Unknown Extension"); } } } - } - - pub fn decode_inst(&self, inst: rv32::Word) -> Option { - // we need to go over every instruction and see if it matches - // we can do smarter things with cacheing later - this aint blazin - for extension in &self.extensions { - match extension.match_inst(inst) { - Some(inst) => return Some(inst), - None => continue, - }; - }; - None - } + Ok(()) + } } diff --git a/src/ext/mod.rs b/src/ext/mod.rs index 5331d4b..7cf3e95 100644 --- a/src/ext/mod.rs +++ b/src/ext/mod.rs @@ -1,6 +1,6 @@ use crate::system::rv32; -mod decode; +pub mod decode; // Instruction bitmasks // This will be awkward as the instruction types @@ -8,5 +8,3 @@ mod decode; // Instruction parsing // Extensibility -type Instruction = rv32::Word; - diff --git a/src/main.rs b/src/main.rs index 92fc027..f02231f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,19 +13,25 @@ mod system; mod inst; use crate::system::bus; +use crate::ext::decode; use crate::cpu::*; struct VMRV32I { bus: Rc>, cpu: cpu::CPU, + instruction_decoder: Rc>, } impl VMRV32I { fn new() -> VMRV32I { + let extensions = vec!['i']; + let bus = Rc::new(RefCell::new(bus::Bus::new())); - let mut cpu = CPU::new(Rc::clone(&bus)); + let instruction_decoder = Rc::new(RefCell::new(decode::DecodeCycle::new(extensions))); + let mut cpu = CPU::new(Rc::clone(&bus), Rc::clone(&instruction_decoder)); + cpu.init(); - VMRV32I { cpu, bus } + VMRV32I { cpu, bus, instruction_decoder } } fn load_prog(&mut self, file: &str) { @@ -62,7 +68,10 @@ impl VMRV32I { } fn dispatch(&mut self) { - self.cpu.exec(); + match self.cpu.exec() { + Ok(_) => println!("VM > Program exited peacefully"), + Err(e) => println!("VM > Program exited violently with error: {}", e), + } } }