-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add first party C tests that can be used for tests specific to this model. This can use Clang or RISC-V GCC to compile the tests. It includes a copy of `nanoprinf` for printing to the UART.
- Loading branch information
Showing
15 changed files
with
7,281 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
add_subdirectory("riscv-tests") | ||
|
||
# This is off by default so we don't require people who | ||
# just want to build the model to have Clang or RISC-V GCC | ||
# installed. | ||
option(FIRST_PARTY_TESTS "Compile & run first party tests (requires Clang or RISC-V GCC).") | ||
if (FIRST_PARTY_TESTS) | ||
add_subdirectory("first_party") | ||
endif() |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Tests | ||
|
||
* `riscv-tests` - a collection of very old pre-compiled ELFs from [the `riscv-tests` repo](https://github.com/riscv-software-src/riscv-tests). These are bare minimum tests; not very exhaustive at all. | ||
* `first_party` - tests specifically designed for this Sail model. These tests are not designed to test all the features of RISC-V. Rather they are for testing new code that we add, and bug fixes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
|
||
# First party tests | ||
|
||
# There are three options for cross-compilation of an RV32 program. | ||
# | ||
# 1. Clang, which is sensibly designed to be able to cross-compile to any architecture it supports. | ||
# Unfortunately it is possible to build it without RISC-V support, e.g. RHEL 8 does this, so we | ||
# need to check that. You can download a binary from https://github.com/llvm/llvm-project/releases | ||
# 2. GCC via riscv32-unknown-elf-gcc. Unlike Clang you have to specifically build GCC as a cross | ||
# compiler. You can download a binary from https://github.com/riscv-collab/riscv-gnu-toolchain/releases | ||
# 3. GCC via riscv64-unknown-elf-gcc, but only if it has "multilib" support, so we'll try riscv32-... first. | ||
|
||
find_program(CLANG_BIN "clang") | ||
find_program(GCC_BIN_RV32 "riscv32-unknown-elf-gcc") | ||
find_program(GCC_BIN_RV64 "riscv64-unknown-elf-gcc") | ||
|
||
if (CLANG_BIN) | ||
message(STATUS "Found clang: ${CLANG_BIN}") | ||
endif() | ||
if (GCC_BIN_RV32) | ||
message(STATUS "Found riscv32-unknown-elf-gcc: ${GCC_BIN_RV32}") | ||
endif() | ||
if (GCC_BIN_RV64) | ||
message(STATUS "Found riscv64-unknown-elf-gcc: ${GCC_BIN_RV64}") | ||
endif() | ||
|
||
set(CROSS_COMPILER_COMMAND_RV32) | ||
set(CROSS_COMPILER_COMMAND_RV64) | ||
|
||
# Prefer Clang. | ||
if (CLANG_BIN) | ||
# Check it supports RISC-V. | ||
execute_process( | ||
COMMAND clang -print-targets | ||
OUTPUT_VARIABLE clang_targets | ||
COMMAND_ERROR_IS_FATAL ANY | ||
) | ||
|
||
# Check if `riscv32` is present in the output | ||
if (clang_targets MATCHES "riscv32") | ||
set(CROSS_COMPILER_COMMAND_RV32 ${CLANG_BIN} --target=riscv32) | ||
set(CROSS_COMPILER_COMMAND_RV64 ${CLANG_BIN} --target=riscv64) | ||
else() | ||
message(WARNING "Your Clang compiler does not support RISC-V (see '${CLANG_BIN} -print-targets'). Please download one from https://github.com/llvm/llvm-project/releases") | ||
endif() | ||
endif() | ||
|
||
# Prefer riscv32-unknown-elf-gcc to riscv64-unknown-elf-gcc for RV32. | ||
if (GCC_BIN_RV32) | ||
if (NOT CROSS_COMPILER_COMMAND_RV32) | ||
set(CROSS_COMPILER_COMMAND_RV32 ${GCC_BIN_RV32}) | ||
endif() | ||
endif() | ||
|
||
if (GCC_BIN_RV64) | ||
if (NOT CROSS_COMPILER_COMMAND_RV32) | ||
# It might support multilib. | ||
set(CROSS_COMPILER_COMMAND_RV32 ${GCC_BIN_RV64}) | ||
endif() | ||
if (NOT CROSS_COMPILER_COMMAND_RV64) | ||
set(CROSS_COMPILER_COMMAND_RV64 ${GCC_BIN_RV64}) | ||
endif() | ||
endif() | ||
|
||
if (NOT CROSS_COMPILER_COMMAND_RV32 OR NOT CROSS_COMPILER_COMMAND_RV64) | ||
message(FATAL_ERROR "No suitable cross-compiler found. We recommend downloading Clang from https://github.com/llvm/llvm-project/releases") | ||
endif() | ||
|
||
set(common_deps | ||
"src/common/crt0.S" | ||
"src/common/encoding.h" | ||
"src/common/link.ld" | ||
"src/common/nanoprintf.c" | ||
"src/common/nanoprintf.h" | ||
"src/common/runtime.c" | ||
"src/common/runtime.h" | ||
) | ||
|
||
set(tests | ||
"test_hello_world.c" | ||
"test_minstret.S" | ||
) | ||
|
||
foreach (xlen IN ITEMS 32 64) | ||
foreach (test_source IN LISTS tests) | ||
set(arch "rv${xlen}d") | ||
if (xlen EQUAL 32) | ||
set(mabi "ilp32") | ||
else() | ||
set(mabi "lp64") | ||
endif() | ||
|
||
set(elf "${arch}_${test_source}.elf") | ||
|
||
add_custom_command( | ||
OUTPUT ${elf} | ||
DEPENDS ${common_deps} "src/${test_source}" | ||
COMMAND ${CROSS_COMPILER_COMMAND_RV${xlen}} | ||
# The ISA string to compile for. | ||
-march=rv${xlen}gc | ||
# Calling convention to use. Valid values are 'ilp32' or 'lp64' for 32/64-bit, | ||
# optionally followed by 'f' or 'd' for hard-float. All combinations are valid. | ||
-mabi=${mabi}d | ||
# Required for compatibility with old versions of LLD. Otherwise you get an error | ||
# "relocation R_RISCV_ALIGN requires unimplemented linker relaxation; recompile with -mno-relax" | ||
-mno-relax | ||
# Indicate we are building in a standalone environment. Implies -fno-builtin | ||
# so the compiler won't assume that memcpy etc. are available. | ||
-ffreestanding | ||
# Don't try to link with libc or libm. | ||
-nostdlib | ||
# Generate a statically linked binary. | ||
-static | ||
# The relocation model. This compiles the code so that it can | ||
# be linked at any address. This means the linker script | ||
# doesn't have to e.g. put all code in the first 2GB of memory. | ||
-mcmodel=medany | ||
# Generate debug info. | ||
-g | ||
# Optimise code generation for a good debugging experience. | ||
-Og | ||
# Enable warnings and upgrade them to errors. | ||
-Wall | ||
-Werror | ||
# The linker script. | ||
-T "${CMAKE_CURRENT_SOURCE_DIR}/src/common/link.ld" | ||
-o ${elf} | ||
"${CMAKE_CURRENT_SOURCE_DIR}/src/${test_source}" | ||
"${CMAKE_CURRENT_SOURCE_DIR}/src/common/crt0.S" | ||
"${CMAKE_CURRENT_SOURCE_DIR}/src/common/nanoprintf.c" | ||
"${CMAKE_CURRENT_SOURCE_DIR}/src/common/runtime.c" | ||
VERBATIM | ||
COMMENT "Compiling ${test_source}" | ||
) | ||
|
||
add_custom_target(build_${arch}_${test_source} ALL DEPENDS ${elf}) | ||
|
||
add_test( | ||
NAME "first_party_${arch}_${test_source}" | ||
COMMAND $<TARGET_FILE:riscv_sim_${arch}> --pmp-count 16 ${elf} | ||
) | ||
endforeach() | ||
endforeach() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
#include "encoding.h" | ||
|
||
#define MSTATUS_MPP_OFFSET 11 | ||
|
||
.section .text | ||
|
||
.global _start | ||
_start: | ||
j reset_handler | ||
|
||
init_pmp: | ||
// Save the current mtvec before changing it | ||
csrr s1, mtvec | ||
// Set a trap handler that would just jump to 1:, in case PMPs are not supported | ||
la t0, 1f | ||
csrw mtvec, t0 | ||
// Set a full match pmp address | ||
li t0, (1 << (31 + (__riscv_xlen / 64) * (53 - 31))) - 1 | ||
csrw pmpaddr0, t0 | ||
// Configure the above pmp to have full access | ||
li t0, PMP_NAPOT | PMP_R | PMP_W | PMP_X | ||
csrw pmpcfg0, t0 | ||
// Fence and flush all virtual addresses | ||
sfence.vma | ||
// Align target address stored in mtvec to 64 bytes for compatibility with CLIC. | ||
.balign 64 | ||
1: | ||
// Restore mtvec to the previous value and return | ||
csrw mtvec, s1 | ||
csrw mcause, x0 | ||
ret | ||
|
||
// Trap handler is stored in mtvec and must be 4-byte aligned for CLINT but | ||
// must be aligned to 2^6 for compatibility with CLIC. | ||
.p2align 6 | ||
.global trap_handler | ||
trap_handler: | ||
// For some reason when compiling with riscv64-unknown-elf-gcc it | ||
// will happily compile `printf()` etc and just assume it's running | ||
// on Linux. This means it does a `write()` syscall which means | ||
// writing the syscall number to a7, and then `ecall`. We can | ||
// emulate Linux for `write()` to stdout or stderr so that | ||
// `printf()` works. | ||
csrr t5, mcause | ||
// Clear bits [xlen-2 .. 16], since CLIC puts some irrelevant stuff there. | ||
li t6, 0x1FFFF | ||
not t6, t6 | ||
srli t6, t6, 1 | ||
not t6, t6 | ||
and t5, t5, t6 | ||
|
||
li t6, CAUSE_USER_ECALL | ||
beq t5, t6, ecall_handler | ||
li t6, CAUSE_SUPERVISOR_ECALL | ||
beq t5, t6, ecall_handler | ||
li t6, CAUSE_MACHINE_ECALL | ||
beq t5, t6, ecall_handler | ||
|
||
// Unhandled trap. | ||
li a0, 1001 | ||
tail htif_exit | ||
|
||
.global ecall_handler | ||
ecall_handler: | ||
// Handle syscalls. | ||
|
||
// We're going to mret from this so mret to the following instructions. | ||
csrr t6, mepc | ||
addi t6, t6, 4 | ||
csrw mepc, t6 | ||
|
||
// Syscall number is in a7, arguments are in a0..a6, return values in | ||
// a0, a1. See https://man7.org/linux/man-pages/man2/syscall.2.html | ||
|
||
// Custom syscalls e.g. to set/get the privilege can be added here like this: | ||
// | ||
// li t5, SYSCALL_SET_PRIVILEGE | ||
// beq a7, t5, syscall_set_privilege | ||
|
||
// syscalls return -1 on failure. | ||
li a0, -1 | ||
mret | ||
|
||
#define FROM_1_TO_31 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 | ||
|
||
.global reset_handler | ||
reset_handler: | ||
// Initialise registers to avoid X issues. | ||
.irp i, FROM_1_TO_31 | ||
li x\i, 0 | ||
.endr | ||
|
||
// Init float registers to avoid X issues. This requires enabling the | ||
// FPU, but we also have to skip it if fmv.w.x is an illegal instruction. | ||
// Note this can be true even if mstatus[FS] is non-zero, to allow for | ||
// emulation. We also restore the original value of mstatus so that | ||
// tests that check its initial value still work. | ||
#ifdef __riscv_flen | ||
la t0, 9f | ||
csrw mtvec, t0 | ||
li t0, MSTATUS_FS | ||
csrrs t0, mstatus, t0 | ||
.irp i, 0, FROM_1_TO_31 | ||
fmv.w.x f\i, x0 | ||
.endr | ||
.p2align 6 | ||
9: | ||
#endif | ||
|
||
// Initialize pmp | ||
call init_pmp | ||
|
||
// Initialise trap handler. | ||
la t0, trap_handler | ||
csrw mtvec, t0 | ||
|
||
// Initialise stack pointer. | ||
csrr a0, mhartid | ||
|
||
la tp, _stack | ||
|
||
# Give each core 128KB of stack and Thread Local Storage. | ||
#define STKSHIFT 17 | ||
add sp, a0, 1 | ||
sll sp, sp, STKSHIFT | ||
add sp, sp, tp | ||
sll a2, a0, STKSHIFT | ||
add tp, tp, a2 | ||
|
||
// Initialize global pointer. | ||
.option push | ||
.option norelax | ||
la gp, __global_pointer$ | ||
.option pop | ||
|
||
// Store a frame pointer on the stack. This is necessary if you don't use | ||
// -fomit-frame-pointer because main() will load it. | ||
// For simplicity unconditionally store 8 bytes even on RV32. | ||
addi sp, sp, -8 | ||
sw zero, 0(sp) | ||
sw zero, 4(sp) | ||
|
||
call main | ||
|
||
// Main return value is in a0 which we pass straight to htif_exit. | ||
tail htif_exit | ||
|
||
|
||
// HTIF (Host Target InterFace) MMIO device. | ||
// | ||
// bitfield htif_cmd : bits(64) = { | ||
// device : 63 .. 56, | ||
// cmd : 55 .. 48, | ||
// payload : 47 .. 0 | ||
// } | ||
// | ||
// The upper byte must be written second if doing two 4-byte writes. | ||
// Device is: | ||
// | ||
// 0 (syscall-proxy): if payload[0] is 1, exit with code payload[..1] | ||
// otherwise do nothing. | ||
// 1 (terminal): if command is 0, terminal input (unimplemented in Sail) | ||
// if command is 1, terminal output (write lowest byte of payload) | ||
|
||
.section .bss.mmio | ||
|
||
// HTIF devices. | ||
|
||
.balign 8 | ||
.global tohost | ||
tohost: | ||
.fill 8 | ||
|
||
// fromhost is not used by Sail but its presence allows the ELF to run on Spike. | ||
.balign 8 | ||
.global fromhost | ||
fromhost: | ||
.fill 8 | ||
|
||
.section .text | ||
|
||
.global htif_exit | ||
htif_exit: | ||
la t0, tohost | ||
sll a0, a0, 1 | ||
or a0, a0, 1 | ||
1: | ||
sw a0, 0(t0) | ||
sw zero, 4(t0) | ||
j 1b | ||
|
||
.global htif_putc | ||
htif_putc: | ||
la t0, tohost | ||
sw a0, 0(t0) | ||
// device=1 (terminal), cmd=1 (output) | ||
li a0, 0x01010000 | ||
sw a0, 4(t0) | ||
ret |
Oops, something went wrong.