4 releases
Uses old Rust 2015
0.2.2 | Mar 21, 2021 |
---|---|
0.2.1 | Nov 14, 2018 |
0.2.0 | Nov 11, 2018 |
0.1.0 | Mar 18, 2018 |
#107 in Emulators
305KB
5K
SLoC
rvsim
A RISC-V simulator implementing RV32G[C], written in Rust.
See the documentation for usage.
Current limitations
- Supports only little-endian hosts.
- Windows support needs work.
Features
serialize
enable serialization supportrv32c
enable RV32C compressed instruction set supportrv32fd
enables RV32F (Single-Precision Floating-Point) and RV32F (Double-Precision Floating-Point) instruction set support (default)
License
Rvsim uses the MIT license, but includes portions of Berkeley SoftFloat, used when the 'rv32fd' feature is enabled (default). Berkely SoftFloat uses the BSD 3-clause license. For details, see the COPYING.md file.
lib.rs
:
A RISC-V simulator implementing RV32G[C].
Usage
The primary workhorse in this crate is the Interp
. It takes a CpuState
, Memory
and
Clock
, then simulates a virtual CPU using these resources. CpuState
is a struct, while
Memory
and Clock
are traits, allowing complete control over the structure of the rest of
the virtual machine.
When using the crate feature serialize
, a CpuState
can be serialized (and deserialized) in
order to suspend a virtual machine to persistent storage.
A very basic ELF parser is also provided in the elf
module. Rvsim itself uses this parser to
run the official RISC-V test suite.
Example
extern crate rvsim;
use std::io::Write;
/// A simple `Memory` implementation, that creates an address space with just some DRAM.
struct SimpleMemory {
dram: Vec<u8>,
}
impl SimpleMemory {
const DRAM_BASE: u32 = 0x1000_0000;
const DRAM_SIZE: usize = 0x10_0000;
fn new() -> Self {
Self { dram: vec![0; Self::DRAM_SIZE] }
}
}
/// Our implementation of `Memory` builds a simple memory map.
///
/// The `Memory` trait is also implemented for `[u8]`, so we can simply delegate to it, after
/// translating the address.
///
/// The condition here only checks the start address of DRAM, because the upper bound is
/// already checked by the `[u8]` implementation. This type of memory map can be easily
/// extended by adding more `else if` clauses, working through blocks of memory from highest
/// base address to lowest.
impl rvsim::Memory for SimpleMemory {
fn access<T: Copy>(&mut self, addr: u32, access: rvsim::MemoryAccess<T>) -> bool {
if addr >= Self::DRAM_BASE {
rvsim::Memory::access(&mut self.dram[..], addr - Self::DRAM_BASE, access)
} else {
false
}
}
}
fn main() {
// Create the `SimpleMemory` and load some code into it.
// Writing to the start of DRAM will put the code at `DRAM_BASE` in the address space.
let mut mem = SimpleMemory::new();
(&mut mem.dram[..]).write_all(&[
0x73, 0x00, 0x10, 0x00 // `EBREAK`
]).unwrap();
// We can use the very basic `Clock` implementation that is provided.
let mut clock = rvsim::SimpleClock::new();
// Create the virtual CPU state, setting the PC to the start of our program.
let mut state = rvsim::CpuState::new(SimpleMemory::DRAM_BASE);
// Run until the program stops.
let mut interp = rvsim::Interp::new(&mut state, &mut mem, &mut clock);
let (err, op) = interp.run();
// The program should've stopped at the `EBREAK` instruction.
assert_eq!(err, rvsim::CpuError::Ebreak);
assert_eq!(op, Some(rvsim::Op::Ebreak));
}
Current limitations
- Supports only little-endian hosts.
- Windows support needs work.
License
Rvsim uses the MIT license, but includes portions of Berkeley SoftFloat, which uses the BSD 3-clause license. For details, see the COPYING.md file.
Dependencies
~0–750KB
~16K SLoC