Fetch Decode Execute for Rtype and Itype

This commit is contained in:
Benjamin Kyd
2023-06-01 22:03:06 +01:00
parent d05d88df00
commit 59ea74e601
4 changed files with 184 additions and 78 deletions

View File

@@ -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);
},
}
}
}

View File

@@ -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>,
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}