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
//! 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
extern crate serde_derive;
pub use *;