CLI, IMA, Linux, UART
Some checks failed
Rust nightly / Check (push) Has been cancelled

This commit is contained in:
Ben Kyd
2023-07-31 00:12:48 +01:00
parent 3bad8ec1e3
commit 1cc1cae6b5
21 changed files with 803 additions and 84 deletions

250
Cargo.lock generated
View File

@@ -2,12 +2,67 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
[[package]]
name = "anstyle-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "bits"
version = "0.1.0"
@@ -16,6 +71,59 @@ dependencies = [
"syn 2.0.18",
]
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "clap"
version = "4.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]]
name = "clap_lex"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "enum_dispatch"
version = "0.3.11"
@@ -28,12 +136,62 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "errno"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix",
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "linux-raw-sys"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
[[package]]
name = "modular-bitfield"
version = "0.11.2"
@@ -160,12 +318,26 @@ name = "riscy-rust"
version = "0.1.0"
dependencies = [
"bits",
"clap",
"enum_dispatch",
"modular-bitfield",
"num",
"strum",
]
[[package]]
name = "rustix"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustversion"
version = "1.0.13"
@@ -178,6 +350,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.25.0"
@@ -227,3 +405,75 @@ name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

View File

@@ -7,6 +7,7 @@ edition = "2021"
path = "lib/bits"
[dependencies]
clap = { version = "4.3.19", features = ["derive"] }
enum_dispatch = "0.3.11"
modular-bitfield = "0.11.2"
num = "0.4.0"

View File

@@ -1,3 +1,5 @@
use std::time::{SystemTime, UNIX_EPOCH};
use crate::ext::decode;
use crate::system::bus::*;
use crate::system::ram;
@@ -31,37 +33,44 @@ pub struct CPUState {
//
// We will also not implement all of them, just the ones we need for the Linux
// TODO: Do all of the CSRs exist on each hart? Or are they per hart?
mstatus: rv32::Word, // Machine status reg to disable interrupts
pub mstatus: rv32::Word, // Machine status reg to disable interrupts
// Timers
cyclel: rv32::Word, // Lower 32 bits of the cycle counter
cycleh: rv32::Word, // Upper 32 bits of the cycle counter
timel: rv32::Word, // Lower 32 bits of the timer
timeh: rv32::Word, // Upper 32 bits of the timer
timecmpl: rv32::Word, // Lower 32 bits of the timer compare register
timecmph: rv32::Word, // Upper 32 bits of the timer compare register
pub cyclel: rv32::Word, // Lower 32 bits of the cycle counter
pub cycleh: rv32::Word, // Upper 32 bits of the cycle counter
pub timel: rv32::Word, // Lower 32 bits of the timer
pub timeh: rv32::Word, // Upper 32 bits of the timer
pub timecmpl: rv32::Word, // Lower 32 bits of the timer compare register
pub timecmph: rv32::Word, // Upper 32 bits of the timer compare register
// Machine Information Registers
mvendorid: rv32::Word, // Vendor ID of the hart
marchid: rv32::Word, // Architecture ID of the hart
mimpid: rv32::Word, // Implementation ID of the hart
mhartid: rv32::Word, // Hardware thread ID of the hart
pub mvendorid: rv32::Word, // Vendor ID of the hart
pub marchid: rv32::Word, // Architecture ID of the hart
pub mimpid: rv32::Word, // Implementation ID of the hart
pub mhartid: rv32::Word, // Hardware thread ID of the hart
// Machine Trap Stuffs
mscratch: rv32::Word, // Scratch register for machine trap handlers
mtvec: rv32::Word, // Address of trap handler
mie: rv32::Word, // Machine interrupt enable
mip: rv32::Word, // Machine interrupt pending
pub mscratch: rv32::Word, // Scratch register for machine trap handlers
pub mtvec: rv32::Word, // Address of trap handler
pub mie: rv32::Word, // Machine interrupt enable
pub mip: rv32::Word, // Machine interrupt pending
mepc: rv32::Word, // Machine exception program counter
mtval: rv32::Word, // Machine trap value
mcause: rv32::Word, // Machine trap cause
pub mepc: rv32::Word, // Machine exception program counter
pub mtval: rv32::Word, // Machine trap value
pub mcause: rv32::Word, // Machine trap cause
// Note: only a few bits are used. (Machine = 3, User = 0)
// Bits 0..1 = privilege.
// Bit 2 = WFI (Wait for interrupt)
// Bit 3+ = Load/Store reservation LSBs.
pub extraflags: rv32::Word,
}
pub struct CPU {
state: CPUState,
instruction_decoder: Rc<RefCell<decode::DecodeCycle>>,
extensions: Vec<char>,
last_it_time: u128,
}
impl CPU {
@@ -94,9 +103,11 @@ impl CPU {
mepc: 0,
mtval: 0,
mcause: 0,
extraflags: 0,
},
instruction_decoder,
extensions,
last_it_time: 0,
}
}
@@ -106,9 +117,19 @@ impl CPU {
println!("-----------------");
println!("VM > Initializing CPU");
self.last_it_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_micros();
self.state.pc = DRAM_BASE as rv32::Word;
self.state.x[0] = 0x00000000; // x0 is tied to ground
self.state.x[2] = DRAM_BASE + ram::DRAM_SIZE as u32; // x2 the stack pointer
self.state.x[2] = DRAM_BASE + DRAM_SIZE as u32; // x2 the stack pointer
self.state.mvendorid = 0x696969; // Vendor ID of the hart
self.state.marchid = 0x285700; // Architecture ID of the hart
self.state.mimpid = 0; // Implementation ID of the hart
self.state.mhartid = 0; // Hardware thread ID of the hart
println!("VM > CPU Initialisd with extensions {:?}", self.extensions);
self.dump_reg();
}
@@ -121,33 +142,119 @@ impl CPU {
self.state.bus.borrow_mut().load_32(self.state.pc)
}
pub fn step(&mut self) -> Result<(), String> {
pub fn step(&mut self, elapsed_micros: u128) -> Result<(), String> {
// CSR stuff before fetch execute
//
let new_timer = ((self.state.timel as u128) + elapsed_micros) as rv32::Word;
if new_timer > self.state.timel {
self.state.timeh += 1;
}
self.state.timel = new_timer;
// handle time interrupt
if self.state.timel >= self.state.timecmpl
&& self.state.timeh <= self.state.timecmph
&& (self.state.timecmpl, self.state.timecmph) != (0, 0)
{
self.state.extraflags &= !4;
self.state.mip |= 1 << 7; // https://stackoverflow.com/a/61916199/2926815 Fire interrupt.
} else {
self.state.mip &= !(1 << 7);
}
// if WFI is set, we exit early
if self.state.extraflags & 4 != 0 {
return Ok(());
}
let mut trap: rv32::Word = 0;
let mut rval: rv32::Word = 0;
let cycle: rv32::Word = self.state.cyclel;
if (self.state.mip & (1 << 7) != 0)
&& (self.state.mie & (1 << 7) != 0/*mtie*/)
&& (self.state.mstatus & 0x8 /*mie*/ != 0)
{
// stall
trap = 0x80000007;
self.state.pc = self.state.pc - rv32::WORD as u32;
} else {
// TODO: We can execute multiple instructions per cycle
// fetch
let inst = self.fetch();
println!("VM > Fetched 0x{:08x}: 0x{:08x}", self.state.pc, inst);
self.state.x[0] = 0x00000000;
self.state.pc = self.state.pc + rv32::WORD as u32;
// decode and execute
self.instruction_decoder
.borrow_mut()
.decode_exec_inst(inst, &mut self.state)?;
self.state.pc = self.state.pc + rv32::WORD as u32;
}
// handle trap and interrupt
if trap != 0 {
if trap & 0x80000000 != 0 {
// interrupt
self.state.mcause = trap;
self.state.mtval = 0;
self.state.pc = self.state.pc + rv32::WORD as u32;
} else {
// trap
self.state.mcause = trap - 1;
self.state.mtval &= if trap > 5 && trap <= 8 {
rval
} else {
self.state.pc
};
}
self.state.mepc = self.state.pc; // the kernel may advance mepc on it's own
// on an interrupt, the system will move MIE into MPIE
self.state.mstatus =
(((self.state.mstatus) & 0x08) << 4) | (((self.state.extraflags) & 3) << 11);
self.state.pc = self.state.mtvec - rv32::WORD as u32;
// enter machine mode
self.state.extraflags |= 3;
self.state.pc = self.state.pc + rv32::WORD as u32;
}
if self.state.cyclel > cycle {
self.state.cycleh += 1;
}
self.state.cyclel = cycle;
Ok(())
}
pub fn exec_step(&mut self) -> Result<(), String> {
let since_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards");
let us = since_epoch.as_micros();
let elapsed = us - self.last_it_time;
self.last_it_time += elapsed;
self.step(elapsed)?;
Ok(())
}
pub fn exec(&mut self) -> Result<(), String> {
while self.state.pc - DRAM_BASE < ram::DRAM_SIZE as u32 {
self.step()?;
self.dump_reg();
while self.state.pc - DRAM_BASE < DRAM_SIZE as u32 {
let since_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards");
let us = since_epoch.as_micros();
let elapsed = us - self.last_it_time;
self.last_it_time += elapsed;
self.step(elapsed)?;
}
Ok(())
}
fn dump_reg(&mut self) {
pub fn dump_reg(&mut self) {
println!("VM > Dumping registers");
println!(" > PC : 0x{:08x}", self.state.pc);
for i in 0..8 {

View File

@@ -0,0 +1,69 @@
use std::usize;
use bits::match_mask;
use enum_dispatch::*;
use strum::EnumIter;
use super::encoding::{GenInstruction, Instruction};
use crate::cpu;
use crate::helpers::sext;
use crate::system::rv32;
// FOR BRANCH INSTRUCTIONS ITS IMPERATIVE TO REMEMBER
// THAT WE INCREMENT PC AFTER THE EXECUTION
#[derive(Default, Copy, Clone)]
pub struct LRW; // LR.W rd, rs1 - Load Reserved Word
// Load a word from memory into rd
// and set the extraflags to rs1
// rd = mem[rs1]
impl Instruction for LRW {
fn name(&self) -> &'static str {
"LR.W"
}
fn match_inst(&self, inst: rv32::Word) -> bool {
match_mask!(inst, "00010xx00000xxxxx010xxxxx0101111")
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
let inst = unsafe { inst.R };
let rs1 = state.x[inst.rs1() as usize];
state.extraflags = (state.extraflags & 0x07) | (rs1 << 3);
state.x[inst.rd() as usize] = sext(state.bus.borrow_mut().load_32(rs1), 32);
}
}
#[derive(Default, Copy, Clone)]
pub struct SCW; // SC.W rd, rs1 - Store Conditional Word
// Store a word from rd into memory
// if the extraflags match rs1
// mem[rs1] = rd
impl Instruction for SCW {
fn name(&self) -> &'static str {
"SC.W"
}
fn match_inst(&self, inst: rv32::Word) -> bool {
match_mask!(inst, "00011xxxxxxxxxxxx010xxxxx0101111")
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
let inst = unsafe { inst.R };
let rs1 = state.x[inst.rs1() as usize];
let write_flag = (state.extraflags >> 3) == (rs1 & 0x1fffffff);
if write_flag {
state.bus.borrow_mut().store_32(rs1, state.x[inst.rs2() as usize]);
state.x[inst.rd() as usize] = 0;
} else {
state.x[inst.rd() as usize] = 1;
}
}
}
#[derive(EnumIter)]
#[enum_dispatch(Instruction)]
pub enum ExtensionA {
LRW(LRW),
SCW(SCW),
}

View File

@@ -4,7 +4,10 @@ use super::encoding::{GenInstruction, Instruction};
use crate::cpu;
use crate::system::rv32;
use crate::ext::a;
use crate::ext::i;
use crate::ext::m;
use crate::ext::z;
pub struct DecodeCycle {
extensions: Vec<char>,
@@ -47,6 +50,21 @@ impl DecodeCycle {
return Ok(());
}
}
'a' => {
if let Some(()) = enumerate_extension::<a::ExtensionA>(inst, state) {
return Ok(());
}
}
'm' => {
if let Some(()) = enumerate_extension::<m::ExtensionM>(inst, state) {
return Ok(());
}
}
'z' => {
if let Some(()) = enumerate_extension::<z::ExtensionZ>(inst, state) {
return Ok(());
}
}
_ => println!("VM > Unknown Extension"),
}
}

View File

@@ -159,3 +159,4 @@ pub union GenInstruction {
pub U: std::mem::ManuallyDrop<UType>,
pub J: std::mem::ManuallyDrop<JType>,
}

View File

@@ -27,7 +27,6 @@ impl Instruction for LUI {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing LUI");
let inst = unsafe { inst.U };
let val = inst.full_imm() << 12;
state.x[inst.rd() as usize] = val;
@@ -47,7 +46,6 @@ impl Instruction for AUIPC {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing AUIPC");
let inst = unsafe { inst.U };
let val = inst.full_imm() << 12;
let pc_add = state.pc.wrapping_add(val);
@@ -69,7 +67,6 @@ impl Instruction for JAL {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing JAL");
let inst = unsafe { inst.J };
let offset = sext(inst.full_imm() << 1, 32);
let pc = offset.wrapping_add(state.pc);
@@ -93,7 +90,6 @@ impl Instruction for JALR {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing JALR");
let inst = unsafe { inst.I };
let offset = sext(inst.full_imm(), 32);
let pc = offset.wrapping_add(state.x[inst.rs1() as usize]);
@@ -116,7 +112,6 @@ impl Instruction for BRANCH {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing BEQ, BNE, BLT, BGE, BLTU, BGEU");
let inst = unsafe { inst.B };
let offset = state.pc + (inst.sext_imm() << 1) - 4;
match inst.funct3() {
@@ -174,7 +169,6 @@ impl Instruction for LOAD {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing LOAD");
let inst = unsafe { inst.I };
let offset = inst.sext_imm();
let addr = state.x[inst.rs1() as usize].wrapping_add(offset);
@@ -213,7 +207,6 @@ impl Instruction for STORE {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing STORE");
let inst = unsafe { inst.S };
let offset = inst.sext_imm();
let addr = state.x[inst.rs1() as usize].wrapping_add(offset);
@@ -266,7 +259,6 @@ impl Instruction for IMM {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing ADDI, SLTI, SLTIU, XORI, ORI, ANDI");
let inst = unsafe { inst.I };
let mut retval = 0;
let rs1 = state.x[inst.rs1() as usize];
@@ -305,7 +297,6 @@ impl Instruction for SHIFTI {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing SLLI, SRLI, SRAI");
let inst = unsafe { inst.R }; // fun7 is the L/A selector
// rs2 is shamt
let mut retval = 0;
@@ -340,7 +331,6 @@ impl Instruction for OP {
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
println!("VM > Executing ADD, SUB, SLI, SLT, SLTU, XOR, SRL, SRA, OR, AND");
let inst = unsafe { inst.R };
let mut retval = 0;
let rs1 = state.x[inst.rs1() as usize];

View File

@@ -0,0 +1,40 @@
use std::usize;
use bits::match_mask;
use enum_dispatch::*;
use strum::EnumIter;
use super::encoding::{GenInstruction, Instruction};
use crate::cpu;
use crate::ext::encoding::ImmediateMode;
use crate::helpers::sext;
use crate::system::rv32;
// FOR BRANCH INSTRUCTIONS ITS IMPERATIVE TO REMEMBER
// THAT WE INCREMENT PC AFTER THE EXECUTION
#[derive(Default, Copy, Clone)]
pub struct MULW; // MULW rd, rs1, rs2 - Multiply Word
// Multiply rs1 and rs2 and store the lower 32 bits in rd
impl Instruction for MULW {
fn name(&self) -> &'static str {
"MULW"
}
fn match_inst(&self, inst: rv32::Word) -> bool {
match_mask!(inst, "xxxxxxxxxxxxxxxxxxxxxxxxx0110111")
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
let inst = unsafe { inst.U };
let val = inst.full_imm() << 12;
state.x[inst.rd() as usize] = val;
}
}
#[derive(EnumIter)]
#[enum_dispatch(Instruction)]
pub enum ExtensionM {
MULW(MULW),
}

View File

@@ -3,6 +3,9 @@ use crate::system::rv32;
pub mod encoding;
pub mod decode;
pub mod i;
pub mod a;
pub mod m;
pub mod z;
// Instruction bitmasks
// This will be awkward as the instruction types

46
src/ext/z/mod.rs Normal file
View File

@@ -0,0 +1,46 @@
use std::usize;
use bits::match_mask;
use enum_dispatch::*;
use strum::EnumIter;
use super::encoding::{GenInstruction, Instruction};
use crate::cpu;
use crate::ext::encoding::ImmediateMode;
use crate::helpers::sext;
use crate::system::rv32;
// ZiCSR - Control and Status Register Instructions
// WILL ALSO MATCH ZIENCEI INSTRUCTIONS
// ALTHOUGH THEY ARE NOT IMPLEMENTED YET
// FOR BRANCH INSTRUCTIONS ITS IMPERATIVE TO REMEMBER
// THAT WE INCREMENT PC AFTER THE EXECUTION
#[derive(Default, Copy, Clone)]
pub struct CSRRW; // CSRRW rd, rs1, csr - Atomic Read/Write CSR
// Read the CSR into rd, then write rs1 into the CSR
// rd = csr
// csr = rs1
impl Instruction for CSRRW {
fn name(&self) -> &'static str {
"CSRRW"
}
fn match_inst(&self, inst: rv32::Word) -> bool {
match_mask!(inst, "xxxxxxxxxxxxxxxxxxxxxxxxx0110111")
}
fn step(&self, inst: GenInstruction, state: &mut cpu::CPUState) {
let inst = unsafe { inst.U };
let val = inst.full_imm() << 12;
state.x[inst.rd() as usize] = val;
}
}
#[derive(EnumIter)]
#[enum_dispatch(Instruction)]
pub enum ExtensionZ {
CSRRW(CSRRW),
}

View File

@@ -6,11 +6,11 @@ use std::io::BufReader;
use std::io::Read;
use std::{cell::RefCell, rc::Rc};
mod management;
mod cpu;
mod err;
mod ext;
mod helpers;
mod management;
mod system;
use crate::cpu::*;
@@ -21,12 +21,11 @@ struct VMRV32I {
bus: Rc<RefCell<bus::Bus>>,
cpu: cpu::CPU,
instruction_decoder: Rc<RefCell<decode::DecodeCycle>>,
manager: management::Management,
}
impl VMRV32I {
fn new() -> VMRV32I {
let extensions = vec!['i'];
let extensions = vec!['i', 'm', 'a', 'z'];
let bus = Rc::new(RefCell::new(bus::Bus::new()));
let instruction_decoder =
@@ -42,7 +41,6 @@ impl VMRV32I {
cpu,
bus,
instruction_decoder,
manager: management::Management::new(),
}
}
@@ -67,6 +65,10 @@ impl VMRV32I {
buffer.len() as u32
}
fn has_load_prog(&self) -> bool {
self.bus.borrow_mut().load_32(bus::DRAM_BASE) != 0
}
fn dump_prog(&mut self, size: u32) {
println!("VM > Dumping program (virtual addresses)");
for i in 0..size {
@@ -81,7 +83,16 @@ impl VMRV32I {
}
fn dump_relavent_memory(&self) {
println!("");
println!("VM > Dumping relavent memory");
}
fn dispatch_step(&mut self) {
match self.cpu.exec_step() {
Ok(_) => (),
Err(e) => println!("VM > Program exited violently with error: {}", e),
}
self.cpu.dump_reg();
}
fn dispatch(&mut self) {
@@ -90,20 +101,78 @@ impl VMRV32I {
Err(e) => println!("VM > Program exited violently with error: {}", e),
}
loop {
self.cpu.dump_reg();
println!("VM > CPU has stalled");
std::thread::sleep(std::time::Duration::from_secs(10));
}
}
}
fn main() {
println!("VM Starting Up");
println!("VM Loading CPU Management Engine");
println!("VM > Loading CPU Management Engine");
let manager = management::Management::new();
println!("VM > Starting Up");
let mut vm = VMRV32I::new();
vm.manager.prompt();
let size = vm.load_prog("./test/test.bin");
vm.dump_prog(size);
let mut should_run = false;
manager
.vm_params()
.iter()
.for_each(|action| match action.action {
management::Action::Load => {
println!("VM > Loading file: {}", action.arg);
vm.load_prog(&action.arg);
}
management::Action::Run => {
println!("VM > Running program");
should_run = true;
}
_ => (),
});
if should_run && vm.has_load_prog() {
vm.dispatch();
vm.dump_relavent_memory();
return;
} else if should_run {
println!("VM > CPU has stalled");
return;
}
println!("VM > No program loaded");
// event loop for interactive mode
loop {
let action = manager.prompt();
match action.action {
management::Action::Load => {
println!("VM > Loading file: {}", action.arg);
vm.load_prog(&action.arg);
}
management::Action::Run => {
println!("VM > Running program");
if !vm.has_load_prog() {
println!("VM > No program loaded");
continue;
}
vm.dispatch();
}
management::Action::Step => {
println!("VM > Stepping program");
if !vm.has_load_prog() {
println!("VM > No program loaded");
continue;
}
vm.dispatch_step();
}
management::Action::Dump => {
println!("VM > Dumping program");
vm.dump_prog(0x100);
}
management::Action::Quit => {
println!("VM > Quitting");
break;
}
_ => (),
}
}
}

View File

@@ -1,9 +1,14 @@
use std::io::{stdin, Write};
use clap::Parser;
#[derive(Debug)]
pub struct VMAction {
action: Action,
pub action: Action,
pub arg: String,
}
#[derive(Debug)]
pub enum Action {
Load,
Step,
@@ -11,22 +16,51 @@ pub enum Action {
Inspect,
Dump,
Reg,
Quit,
}
pub struct Management {
pause: bool,
}
#[derive(Parser, Debug)]
#[command(name = "Riscy-Rust, a RV32ima Virtual Machine")]
#[command(version = "0.1.0")]
#[command(author = "Ben Kyd <benjaminkyd@gmail>")]
struct Cli {
#[arg(short, long, value_name = "FILE")]
load: Option<String>,
#[arg(short, long)]
run: bool,
}
impl Management {
pub fn new() -> Management {
Management { pause: true }
}
pub fn vm_params() -> Vec<VMAction> {
vec![VMAction { action: Action::Load }]
pub fn vm_params(&self) -> Vec<VMAction> {
let cli = Cli::parse();
let mut actions = Vec::new();
match cli.load {
Some(file) => actions.push(VMAction {
action: Action::Load,
arg: file,
}),
None => (),
}
pub fn prompt(&self) -> Action {
if cli.run {
actions.push(VMAction {
action: Action::Run,
arg: String::new(),
})
}
actions
}
pub fn prompt(&self) -> VMAction {
print!("VM >> ");
std::io::stdout().flush().unwrap();
@@ -35,12 +69,33 @@ impl Management {
let mut parts = input.trim().split_whitespace();
let command = parts.next().unwrap();
let args = parts;
let mut args = parts;
match command {
"load" => Action::Load,
"step" => Action::Step,
"run" => Action::Run,
"load" => VMAction {
action: Action::Load,
arg: args.next().unwrap().to_string(),
},
"step" => VMAction {
action: Action::Step,
arg: String::new(),
},
"run" => VMAction {
action: Action::Run,
arg: String::new(),
},
"quit" | "q" => VMAction {
action: Action::Quit,
arg: String::new(),
},
"help" | "h" => {
println!("VM > Commands:");
println!("VM > load <file> - load a program into memory");
println!("VM > step - step through the program");
println!("VM > run - run the program");
println!("VM > quit - quit the program");
self.prompt()
}
_ => {
println!("VM > Command {} not found", command);
self.prompt()

View File

@@ -1,62 +1,68 @@
pub const DRAM_BASE: u32 = 0x80000000;
pub const DRAM_SIZE: u32 = 1 * 1024 * 1024 * 1024; // 1GBram
pub const DRAM_TOP: u32 = DRAM_BASE + DRAM_SIZE;
pub const UART_BASE: u32 = 0x10000000;
pub const UART_SIZE: u32 = 0x100;
pub const UART_TOP: u32 = UART_BASE + UART_SIZE;
use crate::system::ram;
use crate::system::rv32;
use crate::system::uart;
pub struct Bus {
memory: ram::RAM,
uart: uart::UART,
}
impl Bus {
pub fn new() -> Bus {
Bus {
memory: ram::RAM::new(),
uart: uart::UART::new(),
}
}
pub fn load_8(&mut self, address: rv32::XLen) -> rv32::Byte {
match address {
DRAM_BASE.. => self.memory.read_8(address),
DRAM_BASE..=DRAM_TOP => self.memory.read_8(address),
_ => {
panic!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
rv32::Byte::default()
}
}
}
pub fn load_16(&mut self, address: rv32::XLen) -> rv32::HalfWord {
match address {
DRAM_BASE.. => self.memory.read_16(address),
DRAM_BASE..=DRAM_TOP => self.memory.read_16(address),
_ => {
panic!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
rv32::HalfWord::default()
}
}
}
pub fn load_32(&mut self, address: rv32::XLen) -> rv32::Word {
match address {
DRAM_BASE.. => self.memory.read_32(address),
UART_BASE..=UART_TOP => self.uart.read_kb(address),
DRAM_BASE..=DRAM_TOP => self.memory.read_32(address),
_ => {
panic!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
rv32::Word::default()
}
}
}
pub fn load_64(&mut self, address: rv32::XLen) -> rv32::DoubleWord {
match address {
DRAM_BASE.. => self.memory.read_64(address),
DRAM_BASE..=DRAM_TOP => self.memory.read_64(address),
_ => {
panic!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
rv32::DoubleWord::default()
}
}
}
pub fn store_8(&mut self, address: rv32::XLen, data: rv32::Byte) {
match address {
DRAM_BASE.. => self.memory.write_8(address, data),
DRAM_BASE..=DRAM_TOP => self.memory.write_8(address, data),
_ => {
panic!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
}
@@ -65,7 +71,7 @@ impl Bus {
pub fn store_16(&mut self, address: rv32::XLen, data: rv32::HalfWord) {
match address {
DRAM_BASE.. => self.memory.write_16(address, data),
DRAM_BASE..=DRAM_TOP => self.memory.write_16(address, data),
_ => {
panic!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
}
@@ -74,7 +80,8 @@ impl Bus {
pub fn store_32(&mut self, address: rv32::XLen, data: rv32::Word) {
match address {
DRAM_BASE.. => self.memory.write_32(address, data),
UART_BASE..=UART_TOP => self.uart.write(address, data),
DRAM_BASE..=DRAM_TOP => self.memory.write_32(address, data),
_ => {
panic!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
}
@@ -83,7 +90,7 @@ impl Bus {
pub fn store_64(&mut self, address: rv32::XLen, data: rv32::DoubleWord) {
match address {
DRAM_BASE.. => self.memory.write_64(address, data),
DRAM_BASE..=DRAM_TOP => self.memory.write_64(address, data),
_ => {
panic!("VM > BUS > Peripheral at 0x{:08x} does not exist", address);
}

View File

@@ -1,3 +1,4 @@
pub mod bus;
pub mod uart;
pub mod ram;
pub mod rv32;

View File

@@ -1,15 +1,12 @@
use crate::system::bus;
use crate::system::rv32;
pub const DRAM_SIZE: u32 = 1 * 1024 * 1024 * 1024; // 1GB
// pub const DRAM_SIZE: u32 = 1 * 1024; // 1KB
pub struct RAM(pub Vec<rv32::Byte>);
impl RAM {
pub fn new() -> RAM {
println!("VM > Initialised RAM with size: {} bytes", DRAM_SIZE);
RAM(vec![0; DRAM_SIZE as usize])
println!("VM > Initialised RAM with size: {} bytes", bus::DRAM_SIZE);
RAM(vec![0; bus::DRAM_SIZE as usize])
}
pub fn len(&mut self) -> usize {

51
src/system/uart.rs Normal file
View File

@@ -0,0 +1,51 @@
use crate::system::bus;
use crate::system::rv32;
pub const UART_TXD: u32 = bus::UART_BASE + 0x00;
pub const UART_RXD: u32 = bus::UART_BASE + 0x05;
fn didkeypress() -> bool {
use std::io::Read;
let mut stdin = std::io::stdin();
let mut buffer = [0; 1];
stdin.read_exact(&mut buffer).unwrap();
buffer[0] != 0
}
fn getkey() -> u8 {
use std::io::Read;
let mut stdin = std::io::stdin();
let mut buffer = [0; 1];
stdin.read_exact(&mut buffer).unwrap();
buffer[0]
}
pub struct UART();
impl UART {
pub fn new() -> UART {
println!("VM > Initialised UART");
UART()
}
pub fn write(&mut self, address: rv32::XLen, value: rv32::Word) {
match address {
UART_TXD => {
print!("{}", value as u8 as char);
}
_ => {
panic!("VM > UART > Peripheral at 0x{:08x} does not exist", address);
}
}
}
pub fn read_kb(&mut self, address: rv32::XLen) -> rv32::Word {
if address != UART_RXD {
return 0x60 | didkeypress() as u8 as rv32::Word;
} else if address == UART_TXD && didkeypress() {
return getkey() as rv32::Word;
}
0
}
}

BIN
test/linux.bin Normal file

Binary file not shown.

BIN
test/rootfs.cpio Normal file

Binary file not shown.

14
test/start-qemu.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
(
BINARIES_DIR="${0%/*}/"
cd ${BINARIES_DIR}
if [ "${1}" = "serial-only" ]; then
EXTRA_ARGS='-nographic'
else
EXTRA_ARGS=''
fi
export PATH="/home/benk/dprog/misc/mini-rv32ima/buildroot/output/host/bin:${PATH}"
exec qemu-system-riscv32 -M virt -bios none -kernel Image -append "rootwait root=/dev/vda ro" -drive file=rootfs.ext2,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -nographic -cpu rv32,mmu=off ${EXTRA_ARGS}
)

BIN
test/test

Binary file not shown.

View File

@@ -10,7 +10,7 @@ SYMBOL TABLE:
00000000 l d .debug_abbrev 00000000 .debug_abbrev
00000000 l d .debug_aranges 00000000 .debug_aranges
00000000 l d .debug_str 00000000 .debug_str
00000000 l df *ABS* 00000000 ccSaJ0Sk.o
00000000 l df *ABS* 00000000 ccKCKnvP.o
80000000 l .text 00000000 main
80000018 l .text 00000000 loop
80000034 l .text 00000000 exit