Skip to content

Commit

Permalink
Add first party C tests
Browse files Browse the repository at this point in the history
Add first party C tests that can be used for tests specific to this model. This uses Clang or RISCV GCC as the cross-compiler.

This includes a copy of `nanoprinf` for printing to the UART.
  • Loading branch information
Timmmm committed Mar 4, 2025
1 parent 2dfc4ff commit f127eb0
Show file tree
Hide file tree
Showing 16 changed files with 7,361 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
run: |
# Ninja is used because the CMake Makefile generator doesn't
# build top-level targets in parallel unfortunately.
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DFIRST_PARTY_TESTS=TRUE
# By default only the rv32d and rv64d emulators are build,
# but we want to build more targets here to ensure they
# can at least build without errors.
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ add_subdirectory("model")
# Emulator binary.
add_subdirectory("c_emulator")

# Old pre-compiled riscv-tests.
add_subdirectory("test/riscv-tests")
# Old pre-compiled riscv-tests & first-party tests.
add_subdirectory("test")

# Release packaging.
if (NOT CPACK_GENERATOR)
Expand Down
9 changes: 9 additions & 0 deletions test/CMakeLists.txt
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()
2 changes: 0 additions & 2 deletions test/README

This file was deleted.

4 changes: 4 additions & 0 deletions test/README.md
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.
130 changes: 130 additions & 0 deletions test/first_party/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@

# 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()
elseif (GCC_BIN_RV64)
set(CROSS_COMPILER_COMMAND_RV64 ${GCC_BIN_RV64})
if (GCC_BIN_RV32)
set(CROSS_COMPILER_COMMAND_RV32 ${GCC_BIN_RV32})
else()
# Hope it has multilib support. I'm not sure how to test that.
set(CROSS_COMPILER_COMMAND_RV32 ${GCC_BIN_RV64})
endif()
else()
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_max_pmp.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
# 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 libc.
-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()
Loading

0 comments on commit f127eb0

Please sign in to comment.