~theonlymrcat/rivet

Toy RV64I emulator written in Zig

d1475cc Add PLIC device

9 days ago

4db116b Check PMPs with splay-tree-backed range map

a month ago

#rivet

Rivet is a toy RISC-V emulator I'm making as an excuse to learn Zig. It currently implements the RV64GC architecture (RV64IMAFDCZicsr_Zifencei), as well as the "Zicntr" and "Zihpm" extensions.

Floating-point support comes from Fabrice Bellard's SoftFP library, used under the MIT licence and modified to support version 2.2 of the RISC-V F/D extension specification. (I had initially tried to use the x86_64 SSE FPU directly, but discovered too late that it's a really hard thing to do, and gave up so as to not burn myself out)

#Building and running

This project is built using Zig 0.13.0. A nix develop dev shell is provided with this version of Zig.

Rivet can be built with zig build. It requires one CLI parameter: an ELF file to load and run. Additional options are documented in the help text (accessed by running without passing any args).

zig build run -- path/to/executable

#Embedded debugger

Rivet comes with a rudimentary debugger. Passing the --debug flag on the command line will show a GDB-like CLI interface where you can control and inspect the state of the emulator. Currently implemented commands include:

  • run: Continue execution until the next breakpoint or watchpoint
  • step: Step into; one instruction
  • next: Step over; one instruction
  • break <symbol_or_address>: Set a breakpoint at the given symbol (as loaded from the ELF) or address (in hexadecimal)
  • watch <line_no>: Set a watchpoint at the given line number of a riscof signature
  • watch <reg_name>=<value>: Set a watchpoint for the given register to equal the given value
  • reg <name>: Print the current contents of the given register
  • csr <name>[=<value>]: Print the current contents of the given CSR (and optionally set it to a given value)
  • trace <event>: Enable trace printing for the chosen event, which can be one of the following:
    • exec: Prints the program counter and the instruction being executed every step
    • trap: Prints the exception code and target privilege whenever an exception or interrupt occurs
    • jump: Prints the previous and next program counter every jump instruction or trap
    • call: Prints function symbol names as they are called, and a0 when c.ret is executed
    • vmem: Prints the result of every virtual memory translation
    • elf: Prints information about the loaded ELF files

#riscv-tests

Rivet can be tested against the tests in riscv-tests, which are included as a submodule in this repository.

git submodule update --init

# Individually:
make -C riscv-tests/isa rv64ui # For ISA tests
make -C riscv-tests/isa rv64um # For M tests
make -C riscv-tests/isa rv64ua # For A tests
make -C riscv-tests/isa rv64uf # For F tests
make -C riscv-tests/isa rv64ud # For D tests
make -C riscv-tests/isa rv64uc # For C tests
make -C riscv-tests/isa rv64mi # For privilege tests
make -C riscv-tests/isa rv64si # For supervisor tests

# Or just:
make -C riscv-tests/isa # For all tests

The built ELF files can then be run with rivet, which will exit with code 0 if all tests passed, or a code indicating which test failed.

The following find command can be used to run tests in bulk, printing the exit code of each test. (Change the -name parameter to choose different tests to run)

zig build
find riscv-tests/isa -executable -name 'rv64*' -exec sh -c 'zig-out/bin/rivet "{}" 2>/dev/null; printf "{}: %s\n" $?' \;

The nix develop dev shell has a riscv-test script which runs the above, but with nicer formatting

riscv-test rv64

#riscof

RISCOF is an architectural test framework that allows RISC-V targets to be tested against a standard reference model. More importantly, it provides a more comprehensive set of ISA tests than the old riscv-tests repo.

Documentation for running these tests is available in the riscof/ directory of this repository.