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::*;