Crate rvsim[−][src]
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.
Modules
elf | A simple copy-free ELF parser. |
softfloat | Berkeley SoftFloat bindings. |
Structs
CpuState | Struct containing all virtual CPU state. |
Interp | The interpeter. |
SimpleClock | A simple implementation of the |
Enums
CpuError | Statuses with which virtual CPU execution may stop. |
MemoryAccess | Types of memory access used with the |
Op | A large enum holding a parsed instruction and its arguments. |
Traits
Clock | A trait used by the interpreter to implement the clock CSRs. |
Memory | A trait used by the interpreter to implement loads and stores. |