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

MIT license

305KB
5K SLoC

C 3K SLoC // 0.4% comments Rust 2K SLoC // 0.1% comments

rvsim

Docs Crate

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 support
  • rv32c enable RV32C compressed instruction set support
  • rv32fd 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