Fetch Decode Execute for Rtype and Itype
This commit is contained in:
40
src/bus.rs
40
src/bus.rs
@@ -4,7 +4,7 @@ use crate::rv32;
|
||||
use crate::ram;
|
||||
|
||||
pub struct Bus {
|
||||
pub memory: ram::RAM,
|
||||
memory: ram::RAM,
|
||||
}
|
||||
|
||||
impl Bus {
|
||||
@@ -59,4 +59,42 @@ impl Bus {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_8(&mut self, address: rv32::XLen, data: rv32::Byte) {
|
||||
match address {
|
||||
DRAM_BASE.. => {
|
||||
self.memory.write_8(address, data)
|
||||
},
|
||||
_ => {
|
||||
println!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn store_16(&mut self, address: rv32::XLen, data: rv32::HalfWord) {
|
||||
match address {
|
||||
_ => {
|
||||
println!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn store_32(&mut self, address: rv32::XLen, data: rv32::Word) {
|
||||
match address {
|
||||
_ => {
|
||||
println!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn store_64(&mut self, address: rv32::XLen, data: rv32::DoubleWord) {
|
||||
match address {
|
||||
_ => {
|
||||
println!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
114
src/inst.rs
114
src/inst.rs
@@ -2,78 +2,110 @@ use modular_bitfield::prelude::*;
|
||||
|
||||
use crate::rv32;
|
||||
|
||||
pub const R_TYPE: u8 = 0b00110011;
|
||||
pub const I_TYPE: u8 = 0b00010011;
|
||||
pub const S_TYPE: u8 = 0b00100011;
|
||||
pub const B_TYPE: u8 = 0b01100011;
|
||||
pub const U_TYPE: u8 = 0b00110111;
|
||||
pub const J_TYPE: u8 = 0b01110011;
|
||||
|
||||
// Null undecided type
|
||||
#[bitfield]
|
||||
#[derive(Debug)]
|
||||
pub struct NullType {
|
||||
pub opcode: B7,
|
||||
pub _undefined: B25,
|
||||
}
|
||||
|
||||
// Arithmetic logic
|
||||
#[bitfield]
|
||||
#[derive(Debug)]
|
||||
pub struct RType {
|
||||
opcode: B7,
|
||||
rd: B5,
|
||||
funct3: B3,
|
||||
rs1: B5,
|
||||
rs2: B5,
|
||||
funct7: B7,
|
||||
pub opcode: B7,
|
||||
pub rd: B5,
|
||||
pub funct3: B3,
|
||||
pub rs1: B5,
|
||||
pub rs2: B5,
|
||||
pub funct7: B7,
|
||||
}
|
||||
|
||||
// Loads & immeiate arithmetic
|
||||
#[bitfield]
|
||||
#[derive(Debug)]
|
||||
pub struct IType {
|
||||
opcode: B7,
|
||||
rd: B5,
|
||||
funct3: B3,
|
||||
rs1: B5,
|
||||
imm: B12,
|
||||
pub opcode: B7,
|
||||
pub rd: B5,
|
||||
pub funct3: B3,
|
||||
pub rs1: B5,
|
||||
pub imm: B12,
|
||||
}
|
||||
|
||||
// Stores
|
||||
#[bitfield]
|
||||
#[derive(Debug)]
|
||||
pub struct SType {
|
||||
opcode: B7,
|
||||
imm_l: B5,
|
||||
funct3: B3,
|
||||
rs1: B5,
|
||||
rs2: B5,
|
||||
imm_h: B7,
|
||||
pub opcode: B7,
|
||||
pub imm_l: B5,
|
||||
pub funct3: B3,
|
||||
pub rs1: B5,
|
||||
pub rs2: B5,
|
||||
pub imm_h: B7,
|
||||
}
|
||||
|
||||
// Conditional jump
|
||||
#[bitfield]
|
||||
#[derive(Debug)]
|
||||
pub struct BType {
|
||||
opcode: B7,
|
||||
imm_11: B1,
|
||||
imm_4_1: B4,
|
||||
funct3: B3,
|
||||
rs1: B5,
|
||||
rs2: B5,
|
||||
imm_10_5: B6,
|
||||
imm_12: B1,
|
||||
pub opcode: B7,
|
||||
pub imm_11: B1,
|
||||
pub imm_4_1: B4,
|
||||
pub funct3: B3,
|
||||
pub rs1: B5,
|
||||
pub rs2: B5,
|
||||
pub imm_10_5: B6,
|
||||
pub imm_12: B1,
|
||||
}
|
||||
|
||||
// Upper immediate
|
||||
#[bitfield]
|
||||
#[derive(Debug)]
|
||||
pub struct UType {
|
||||
opcode: B7,
|
||||
rd: B5,
|
||||
imm: B20,
|
||||
pub opcode: B7,
|
||||
pub rd: B5,
|
||||
pub imm: B20,
|
||||
}
|
||||
|
||||
|
||||
// Unconditional jump
|
||||
#[bitfield]
|
||||
#[derive(Debug)]
|
||||
pub struct JType {
|
||||
opcode: B7,
|
||||
rd: B5,
|
||||
imm_19_12: B8,
|
||||
imm_11: B1,
|
||||
imm_10_1: B10,
|
||||
imm_20: B1,
|
||||
pub opcode: B7,
|
||||
pub rd: B5,
|
||||
pub imm_19_12: B8,
|
||||
pub imm_11: B1,
|
||||
pub imm_10_1: B10,
|
||||
pub imm_20: B1,
|
||||
}
|
||||
|
||||
/* #[derive(Debug)]
|
||||
pub enum Decode {
|
||||
null(std::mem::ManuallyDrop<NullType>),
|
||||
R(std::mem::ManuallyDrop<RType>),
|
||||
I(std::mem::ManuallyDrop<IType>),
|
||||
S(std::mem::ManuallyDrop<SType>),
|
||||
B(std::mem::ManuallyDrop<BType>),
|
||||
U(std::mem::ManuallyDrop<UType>),
|
||||
J(std::mem::ManuallyDrop<JType>),
|
||||
} */
|
||||
|
||||
#[repr(align(8))]
|
||||
pub union Instruction {
|
||||
pub inst: rv32::Word,
|
||||
pub r: std::mem::ManuallyDrop<RType>,
|
||||
pub i: std::mem::ManuallyDrop<IType>,
|
||||
pub s: std::mem::ManuallyDrop<SType>,
|
||||
pub b: std::mem::ManuallyDrop<BType>,
|
||||
pub u: std::mem::ManuallyDrop<UType>,
|
||||
pub j: std::mem::ManuallyDrop<JType>,
|
||||
pub null: std::mem::ManuallyDrop<NullType>,
|
||||
pub R: std::mem::ManuallyDrop<RType>,
|
||||
pub I: std::mem::ManuallyDrop<IType>,
|
||||
pub S: std::mem::ManuallyDrop<SType>,
|
||||
pub B: std::mem::ManuallyDrop<BType>,
|
||||
pub U: std::mem::ManuallyDrop<UType>,
|
||||
pub J: std::mem::ManuallyDrop<JType>,
|
||||
}
|
||||
|
||||
75
src/main.rs
75
src/main.rs
@@ -10,6 +10,20 @@ mod rv32;
|
||||
|
||||
use crate::bus::*;
|
||||
|
||||
// Register ABI Description Saver
|
||||
// x0 zero Zero constant —
|
||||
// x1 ra Return address Callee
|
||||
// x2 sp Stack pointer Callee
|
||||
// x3 gp Global pointer —
|
||||
// x4 tp Thread pointer —
|
||||
// x5 t0 Temporary link Caller
|
||||
// x6-x7 t1-t2 Temporaries Caller
|
||||
// x8 s0 / fp Saved / frame pointer Callee
|
||||
// x9 s1 Saved register Callee
|
||||
// x10-x11 a0-a1 Fn args/return values Caller
|
||||
// x12-x17 a2-a7 Fn args Caller
|
||||
// x18-x27 s2-s11 Saved registers Callee
|
||||
// x28-x31 t3-t6 Temporaries Caller
|
||||
struct VMRV32I {
|
||||
// 32 bi bus
|
||||
bus: bus::Bus,
|
||||
@@ -40,7 +54,7 @@ impl VMRV32I {
|
||||
|
||||
// put program at the base of DRAM
|
||||
for i in 0..buffer.len() {
|
||||
self.bus.memory.0[i] = buffer[i];
|
||||
self.bus.store_8(i as u32 + bus::DRAM_BASE, buffer[i]);
|
||||
}
|
||||
|
||||
println!("VM > Program loaded to 0x{:08x}", self.pc);
|
||||
@@ -49,7 +63,23 @@ impl VMRV32I {
|
||||
fn dump_prog(&mut self) {
|
||||
println!("VM > Dumping program (virtual addresses)");
|
||||
for i in 0..12 {
|
||||
println!("VM > 0x{:08x}: 0x{:02x}", i, self.bus.memory.0[i]);
|
||||
println!(
|
||||
"VM > 0x{:08x}: 0x{:02x}",
|
||||
i,
|
||||
self.bus.load_8(i + bus::DRAM_BASE)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_reg(&mut self) {
|
||||
println!("VM > Dumping registers");
|
||||
println!("PC : 0x{:08x}", self.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]);
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +91,7 @@ impl VMRV32I {
|
||||
self.bus = Bus::new();
|
||||
self.pc = DRAM_BASE as rv32::Word;
|
||||
self.x[0] = 0x00000000; // x0 is tied to ground
|
||||
self.x[2] = self.bus.memory.len() as u32; // x2 the addressable space
|
||||
self.x[2] = ram::DRAM_SIZE as u32; // x2 the addressable
|
||||
}
|
||||
|
||||
fn fetch(&mut self) -> inst::Instruction {
|
||||
@@ -71,19 +101,46 @@ impl VMRV32I {
|
||||
}
|
||||
|
||||
fn exec(&mut self) {
|
||||
while self.pc > self.bus.memory.len() as u32 {
|
||||
while self.pc > 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;
|
||||
|
||||
//decode
|
||||
|
||||
// exec
|
||||
|
||||
self.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()),
|
||||
}
|
||||
}
|
||||
_ => println!("VM > OPCODE {:08b} not implemented", opcode),
|
||||
};
|
||||
|
||||
self.dump_reg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
src/ram.rs
33
src/ram.rs
@@ -3,7 +3,7 @@ use crate::rv32;
|
||||
|
||||
//pub const DRAM_SIZE: u32 = 1 * 1024 * 1024 * 1024; // 1GB
|
||||
//pub const DRAM_SIZE: u32 = 1 * 1024; // 1KB
|
||||
pub const DRAM_SIZE: u32 = 16;
|
||||
pub const DRAM_SIZE: u32 = 12;
|
||||
|
||||
pub struct RAM(pub Vec<rv32::Byte>);
|
||||
|
||||
@@ -54,30 +54,9 @@ impl RAM {
|
||||
ret
|
||||
}
|
||||
|
||||
//pub fn read<T>(&mut self, address: rv32::XLen) -> T
|
||||
//where
|
||||
//T: num::Num
|
||||
//+ num::ToPrimitive
|
||||
//+ Default
|
||||
//+ std::fmt::LowerHex
|
||||
//+ std::ops::Shl<T, Output = T>
|
||||
//+ std::ops::BitOr<T, Output = T>
|
||||
//+ num::cast::NumCast,
|
||||
//{
|
||||
//let address: usize = (address - bus::DRAM_BASE) as usize;
|
||||
//let memory = &self.0;
|
||||
|
||||
//(address..)
|
||||
//.take(core::mem::size_of::<T>())
|
||||
//.enumerate()
|
||||
//.fold(T::default(), |mut acc, (i, x)| {
|
||||
//println!("VM > Reading from 0x{:08x} to 0x{:08x}", x, acc);
|
||||
//println!("VM > Memory: 0x{:02x}", memory[x]);
|
||||
//println!("VM > Now Shift: {}", i * 8);
|
||||
|
||||
//acc << u32::from(i as u32 * 8) | memory[x].from()
|
||||
//})
|
||||
//}
|
||||
|
||||
//pub fn write<T>(&mut self, address: rv32::XLen, data: T) {}
|
||||
pub fn write_8(&mut self, address: rv32::XLen, data: rv32::Byte) {
|
||||
let memory = &mut self.0;
|
||||
let address = (address - bus::DRAM_BASE) as usize;
|
||||
memory[address] = data;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user