Open cnlohr opened 1 year ago
i v got the same question with u, bro.
The existing testing framework makes it really unclear. I really wish there was like a C file somewhere that #include
d pertinent tests instead of needing to go through the build system that makes a lot of assumptions that don't hold true for me.
Here are the steps I use to compile and run the tests for my emulator. It depends on the riscv32-unknown-elf-gcc compiler which comes from the riscv-gnu-toolchain repo (and which was a total pain to compiler for the 32 bit version).
1) Download the repo and populate /env/
submodule
2) Update /env/p/link.ld
with address you want the code to load and execute at (default is 0x80000000)
3) Move your terminal into the /isa/
directory
4) Edit the Makefrag
file within the rv32ui/
directory to comment out the rv32ui_v_tests
line (specifically if you just want to just test the fundamental opcodes and aren't messing with virtual memory stuff, the compilation breaks on these tests if I don't do it)
5) Call the command make rv32ui XLEN=32
to build the rv32ui directory's tests. The output will be ELF binaries and disassembly dumps (useful for debugging) in the /isa/
directory
6) Use the command riscv32-unknown-elf-objcopy -O ihex rv32ui-p-add rv32ui-p-add.hex
to turn the selected ELF binary into an Intel Hex file for loading into an emulator (mine is designed to take these hex files since they define the loading address and the execution start pointer)
6.5) Alternatively, if a raw non-ELF binary is required, you can run the command riscv32-unknown-elf-objcopy -O binary rv32ui-p-add rv32ui-p-add.bin
to generate bin files that you can directly load into your emulator (but you will need to know where in memory to load them since that information is not encoded within them, this should be the address from step 2)
7) Repeat step 6 (or 6.5) again for every test you want to run
8) Load the .hex or .bin file into your emulator and run the tests. The tests will be over once the program enters the infinite loop in the last line of the write_tohost
subroutine (can be seen near the top of the disassembly dump, subroutine definition found in /env/p/riscv_test.h
), register x17 should equal 93 at this point. If the test was successful, then register x3 should equal 1 and register x10 should equal 0. If the test failed, then register x3 will contain the number of the failed test shifted by 1 and ORed by 1 (shift right once to get the failed test number, useful for debugging)
9) The tests heavily depend on correct branching, ECALL
behavior, and CSRs mtvec
, mcause
, and mepc
; along with the MRET
instruction from the privileged instruction set. As such, steps 4-8 should be repeated with the rv32mi tests as well (rv32ui and rv32mi seem to be the minimum required tests for a minimum RV32I implementation)
10) All other extensions can be tested as you see fit based on what's been added to your RV32 implementation. In theory, RV64 should be basically the same. The rv32um and rv32ua tests should be enough to validate an RV32IMA implementation along with the 2 base tests from step 9
I hope this helps you or anyone else in the future. I agree that the documentation isn't filled in enough at the moment, it took me a few days of reverse engineering to get this far, along with a ridiculous amount of trial-n-error and looking at blog posts from years back.
Update: I forgot to say that at the time of writing this the /isa/rv32mi/scall.S
test appears to be fundamentally flawed and will always return error or an invalid state. Also the /isa/rv32ui/simple.S
test should be the first one to run as it tests if the emulator can actually run the tests, if this one doesn't succeed then there's little chance that any of the other ones will succeed.
I can't figure out what mechanism is used to select what test suite is actually compiled.
Or, what toolchain is used. For instance I am using the
buildroot
toolchain.Additionally, I don't see how to test regular machine test for a
rv32ima
, nothing special with user/system/etc.Any pointers? Any suggestions on what a more advanced
./configure
could look like? That would be really helpful to document on the main readme.