that's kinda neat
This commit is contained in:
35
Cargo.lock
generated
35
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
119
src/cpu/mod.rs
119
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<RefCell<Bus>>,
|
||||
instruction_decoder: Rc<RefCell<decode::DecodeCycle>>,
|
||||
// extensions:
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
pub fn new(bus: Rc<RefCell<Bus>>) -> CPU {
|
||||
pub fn new(bus: Rc<RefCell<Bus>>, instruction_decoder: Rc<RefCell<decode::DecodeCycle>>) -> 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!("");
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
extensions: Vec<char>,
|
||||
}
|
||||
|
||||
impl DecodeCycle {
|
||||
pub fn new(&mut self, ext: Vec<char>) {
|
||||
for extension in ext {
|
||||
pub fn new(ext: Vec<char>) -> 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<Instruction> {
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
15
src/main.rs
15
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<RefCell<bus::Bus>>,
|
||||
cpu: cpu::CPU,
|
||||
instruction_decoder: Rc<RefCell<decode::DecodeCycle>>,
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user