1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
#![warn(missing_docs)] #![allow(clippy::doc_markdown)] //! 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. //! //! [COPYING.md]: https://github.com/stephank/rvsim/blob/main/COPYING.md #[cfg(feature = "serialize")] #[macro_use] extern crate serde_derive; #[allow(unused_parens)] mod cpu; pub mod elf; #[cfg(feature = "rv32fd")] pub mod softfloat; pub use cpu::*;