This commit is contained in:
250
Cargo.lock
generated
250
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
173
src/cpu/mod.rs
173
src/cpu/mod.rs
@@ -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;
|
||||
|
||||
// TODO: We can execute multiple instructions per cycle
|
||||
let inst = self.fetch();
|
||||
println!("VM > Fetched 0x{:08x}: 0x{:08x}", self.state.pc, inst);
|
||||
self.state.x[0] = 0x00000000;
|
||||
// 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);
|
||||
}
|
||||
|
||||
self.state.pc = self.state.pc + rv32::WORD as u32;
|
||||
// if WFI is set, we exit early
|
||||
if self.state.extraflags & 4 != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.instruction_decoder
|
||||
.borrow_mut()
|
||||
.decode_exec_inst(inst, &mut self.state)?;
|
||||
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;
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,3 +159,4 @@ pub union GenInstruction {
|
||||
pub U: std::mem::ManuallyDrop<UType>,
|
||||
pub J: std::mem::ManuallyDrop<JType>,
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
@@ -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
46
src/ext/z/mod.rs
Normal 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),
|
||||
}
|
||||
|
||||
99
src/main.rs
99
src/main.rs
@@ -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 {
|
||||
println!("VM > CPU has stalled");
|
||||
std::thread::sleep(std::time::Duration::from_secs(10));
|
||||
}
|
||||
self.cpu.dump_reg();
|
||||
|
||||
println!("VM > CPU has stalled");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
vm.dispatch();
|
||||
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;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 => (),
|
||||
}
|
||||
|
||||
if cli.run {
|
||||
actions.push(VMAction {
|
||||
action: Action::Run,
|
||||
arg: String::new(),
|
||||
})
|
||||
}
|
||||
actions
|
||||
}
|
||||
|
||||
pub fn prompt(&self) -> Action {
|
||||
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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod bus;
|
||||
pub mod uart;
|
||||
pub mod ram;
|
||||
pub mod rv32;
|
||||
|
||||
@@ -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
51
src/system/uart.rs
Normal 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
BIN
test/linux.bin
Normal file
Binary file not shown.
BIN
test/rootfs.cpio
Normal file
BIN
test/rootfs.cpio
Normal file
Binary file not shown.
14
test/start-qemu.sh
Normal file
14
test/start-qemu.sh
Normal 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}
|
||||
)
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user