Spike RISC-V ISA Simulator
About
Spike, the RISC-V ISA Simulator, implements a functional model of one or more RISC-V harts. It is named after the golden spike used to celebrate the completion of the US transcontinental railway.
Spike supports the following RISC-V ISA features: - RV32I and RV64I base ISAs, v2.1 - RV32E and RV64E base ISAs, v1.9 - Zifencei extension, v2.0 - Zicsr extension, v2.0 - Zicntr extension, v2.0 - M extension, v2.0 - A extension, v2.1 - B extension, v1.0 - F extension, v2.2 - D extension, v2.2 - Q extension, v2.2 - C extension, v2.0 - Zbkb, Zbkc, Zbkx, Zknd, Zkne, Zknh, Zksed, Zksh scalar cryptography extensions (Zk, Zkn, and Zks groups), v1.0 - Zkr virtual entropy source emulation, v1.0 - V extension, v1.0 (requires a 64-bit host) - P extension, v0.9.2 - Zba extension, v1.0 - Zbb extension, v1.0 - Zbc extension, v1.0 - Zbs extension, v1.0 - Zfh and Zfhmin half-precision floating-point extensions, v1.0 - Zfa extension, v1.0 - Zfinx extension, v1.0 - Zmmul integer multiplication extension, v1.0 - Zicbom, Zicbop, Zicboz cache-block maintenance extensions, v1.0 - Conformance to both RVWMO and RVTSO (Spike is sequentially consistent) - Machine, Supervisor, and User modes, v1.11 - Hypervisor extension, v1.0 - Svnapot extension, v1.0 - Svpbmt extension, v1.0 - Svinval extension, v1.0 - Svadu extension, v1.0 - Svade extension, v1.0 - Sdext extension, v1.0-STABLE - Sdtrig extension, v1.0-STABLE - Smepmp extension v1.0 - Smstateen extension, v1.0 - Smdbltrp extension, v1.0 - Sscofpmf v0.5.2 - Ssdbltrp extension, v1.0 - Ssqosid extension, v1.0 - Zaamo extension, v1.0 - Zalrsc extension, v1.0 - Zabha extension, v1.0 - Zacas extension, v1.0 - Zawrs extension, v1.0 - Zicfiss extension, v1.0 - Zicfilp extension, v1.0 - Zca extension, v1.0 - Zcb extension, v1.0 - Zcf extension, v1.0 - Zcd extension, v1.0 - Zcmp extension, v1.0 - Zcmt extension, v1.0 - Zfbfmin extension, v0.6 - Zvfbfmin extension, v0.6 - Zvfbfwma extension, v0.6 - Zvbb extension, v1.0 - Zvbc extension, v1.0 - Zvkg extension, v1.0 - Zvkned extension, v1.0 - Zvknha, Zvknhb extension, v1.0 - Zvksed extension, v1.0 - Zvksh extension, v1.0 - Zvkt extension, v1.0 - Zvkn, Zvknc, Zvkng extension, v1.0 - Zvks, Zvksc, Zvksg extension, v1.0 - Zicond extension, v1.0 - Zilsd extension, v1.0 - Zclsd extension, v1.0 - Zimop extension, v1.0
Versioning and APIs
Projects are versioned primarily to indicate when the API has been extended or rendered incompatible. In that spirit, Spike aims to follow the SemVer versioning scheme, in which major version numbers are incremented when backwards-incompatible API changes are made; minor version numbers are incremented when new APIs are added; and patch version numbers are incremented when bugs are fixed in a backwards-compatible manner.
Spike's principal public API is the RISC-V ISA. The C++ interface to Spike's internals is not considered a public API at this time, and backwards-incompatible changes to this interface will be made without incrementing the major version number.
Build Steps
We assume that the RISCV environment variable is set to the RISC-V tools install path.
$ apt-get install device-tree-compiler libboost-regex-dev libboost-system-dev
$ mkdir build
$ cd build
$ ../configure --prefix=$RISCV
$ make
$ [sudo] make install
If your system uses the yum package manager, you can substitute
yum install dtc for the first step.
Build Steps on OpenBSD
Install bash, gmake, dtc, and use clang.
$ pkg_add bash gmake dtc
$ exec bash
$ export CC=cc; export CXX=c++
$ mkdir build
$ cd build
$ ../configure --prefix=$RISCV
$ gmake
$ [doas] make install
Compiling and Running a Simple C Program
Install spike (see Build Steps), riscv-gnu-toolchain, and riscv-pk.
Write a short C program and name it hello.c. Then, compile it into a RISC-V ELF binary named hello:
$ riscv64-unknown-elf-gcc -o hello hello.c
Now you can simulate the program atop the proxy kernel:
$ spike pk hello
Simulating a New Instruction
Adding an instruction to the simulator requires two steps:
-
Describe the instruction's functional behavior in the file riscv/insns/
.h. Examine other instructions in that directory as a starting point. -
Add the opcode and opcode mask to riscv/opcodes.h. Alternatively, add it to the riscv-opcodes package, and it will do so for you:
$ cd ../riscv-opcodes $ vi opcodes // add a line for the new instruction $ make install -
Add the instruction to riscv/riscv.mk.in. Otherwise, the instruction will not be included in the build and will be treated as an illegal instruction.
-
Rebuild the simulator.
Interactive Debug Mode
To invoke interactive debug mode, launch spike with -d:
$ spike -d pk hello
To see the contents of an integer register (0 is for core 0):
: reg 0 a0
To see the contents of a floating point register:
: fregs 0 ft0
or:
: fregd 0 ft0
depending upon whether you wish to print the register as single- or double-precision.
To see the contents of a memory location (physical address in hex):
: mem 2020
To see the contents of memory with a virtual address (0 for core 0):
: mem 0 2020
You can advance by one instruction by pressing the enter key. You can also execute until a desired equality is reached:
: until pc 0 2020 (stop when pc=2020)
: until reg 0 mie a (stop when register mie=0xa)
: until mem 2020 50a9907311096993 (stop when mem[2020]=50a9907311096993)
Alternatively, you can execute as long as an equality is true:
: while mem 2020 50a9907311096993
You can continue execution indefinitely by:
: r
At any point during execution (even without -d), you can enter the
interactive debug mode with <control>-<c>.
To end the simulation from the debug prompt, press <control>-<c> or:
: q
Debugging With Gdb
An alternative to interactive debug mode is to attach using gdb. Because spike tries to be like real hardware, you also need OpenOCD to do that. We'll use the following test program:
$ cat rot13.c
#include <stdio.h>
char text[] = "Vafgehpgvba frgf jnag gb or serr!";
// Don't use the stack, because sp isn't set up.
volatile int wait = 1;
int main()
{
int i = 0;
while (text[i]) {
char lower = text[i] | 32;
if (lower >= 'a' && lower <= 'm')
text[i] += 13;
else if (lower > 'm' && lower <= 'z')
text[i] -= 13;
i++;
}
done:
printf("decoded text: %s\n", text);
}
$ riscv64-unknown-elf-gcc -g -Og --specs=semihost.specs -o rot13 rot13.c
To debug this program, first run spike telling it to listen for OpenOCD:
$ spike --rbb-port=9824 -m0x10000:0x20000 rot13
Listening for remote bitbang connection on port 9824.
...
In a separate shell run OpenOCD with the appropriate configuration file:
$ cat spike.cfg
adapter driver remote_bitbang
remote_bitbang host localhost
remote_bitbang port 9824
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0xdeadbeef
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
gdb report_data_abort enable
init
arm semihosting enable
halt
$ openocd -f spike.cfg
Open On-Chip Debugger 0.12.0
...
Info : starting gdb server for riscv.cpu on 3333
Info : Listening on port 3333 for gdb connections
riscv.cpu halted due to debug-request. Semihosting is active.
...
riscv.cpu: target state: halted
In yet another shell, start your gdb debug session:
$ riscv64-unknown-elf-gdb rot13
...
Reading symbols from rot13...
(gdb) target extended-remote localhost:3333
...
(gdb) load
...
(gdb) set $sp=0x2fff0
(gdb) b main
Breakpoint 1 at 0x10202: file rot13.c, line 5.
(gdb) c
Continuing.
Disabling abstract command writes to CSRs.
Breakpoint 1, main () at rot13.c:5
5 {
(gdb) print text
$1 = "Vafgehpgvba frgf jnag gb or serr!"
(gdb) until done
[riscv.cpu] Found 4 triggers
main () at rot13.c:16
16 printf("decoded text: %s\n", text);
(gdb) c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00019ff8 in _exit ()
(gdb)
...
On the OpenOCD terminal you will have:
...
decoded text: Instruction sets want to be free!
semihosting: *** application exited with 0 ***
riscv.cpu halted due to breakpoint. Semihosting is active.
