Skip to content

Change XLEN and FLEN to be configure-time options #870

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions .github/workflows/compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,7 @@ jobs:
# 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 -DFIRST_PARTY_TESTS=TRUE
# By default only the rv32d and rv64d emulators are built,
# but we want to build more targets here to ensure they
# can at least build without errors.
ninja -C build all riscv_sim_rv32f riscv_sim_rv64f generated_smt_rv64f generated_docs_rv64d
# These targets do not work with Sail 0.18: generated_rmem_rv32d_rmem generated_coq_rv64f_coq
ninja -C build all generated_sail_riscv_docs generated_smt_rv64d generated_smt_rv32d
ctest --test-dir build --output-junit tests.xml --output-on-failure

- name: Upload test results
Expand Down
6 changes: 2 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ execute_process(
)
message(STATUS "Sail library directory: ${sail_dir}")

set(DEFAULT_ARCHITECTURES "rv32d;rv64d" CACHE STRING "Architectures to build by default (rv32f|rv64f|rv32d|rv64d)? " )

option(COVERAGE "Compile with Sail coverage collection enabled.")

# Softfloat support.
Expand Down Expand Up @@ -178,8 +176,8 @@ endif()
include(CPack)

# Convenience targets.
add_custom_target(csim DEPENDS riscv_sim_rv32d riscv_sim_rv64d)
add_custom_target(check DEPENDS generated_model_rv32d generated_model_rv64d)
add_custom_target(csim DEPENDS sail_riscv_sim)
add_custom_target(check DEPENDS generated_model_rv)

# TODO: Add `interpret` target.
# TODO: Add hol4 target.
4 changes: 2 additions & 2 deletions Makefile.old
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ SAIL_RVFI_SRCS = $(addprefix model/,$(SAIL_ARCH_RVFI_SRCS) $(SAIL_SEQ_INST_SRCS)
SAIL_COQ_SRCS = $(addprefix model/,$(SAIL_ARCH_SRCS) $(SAIL_SEQ_INST_SRCS) $(SAIL_OTHER_SRCS) $(SAIL_OTHER_COQ_SRCS))

SAIL_FLAGS += --require-version 0.19.1
SAIL_FLAGS += --config config/default.json
SAIL_FLAGS += --config config/rv64d.json
SAIL_FLAGS += --strict-var
SAIL_FLAGS += -dno_cast
SAIL_DOC_FLAGS ?= -doc_embed plain
Expand Down Expand Up @@ -301,7 +301,7 @@ endif

generated_definitions/lem/$(ARCH)/riscv.lem: $(SAIL_SRCS) Makefile.old
mkdir -p generated_definitions/lem/$(ARCH) generated_definitions/isabelle/$(ARCH)
$(SAIL) $(SAIL_FLAGS) --config config/default.json -lem -lem_output_dir generated_definitions/lem/$(ARCH) -isa_output_dir generated_definitions/isabelle/$(ARCH) -o riscv -lem_lib Riscv_extras -lem_lib Riscv_extras_fdext $(SAIL_SRCS)
$(SAIL) $(SAIL_FLAGS) --config config/rv64d.json -lem -lem_output_dir generated_definitions/lem/$(ARCH) -isa_output_dir generated_definitions/isabelle/$(ARCH) -o riscv -lem_lib Riscv_extras -lem_lib Riscv_extras_fdext $(SAIL_SRCS)
echo "declare {isabelle} rename field sync_exception_ext = sync_exception_ext_exception" >> generated_definitions/lem/$(ARCH)/riscv_types.lem

# sed -i isn't posix compliant, unfortunately
Expand Down
17 changes: 4 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,10 @@ Install [Sail](https://github.com/rems-project/sail/). On Linux you can download
$ ./build_simulators.sh
```

will build the simulators in `build/c_emulator/riscv_sim_rv{32,64}d`.
will build the simulator at `build/c_emulator/sail_riscv_sim`.

If you get an error message saying `sail: unknown option '--require-version'.` it's because your Sail compiler is too old. You need version 0.19.1 or later.

By default the RV32D and RV64D emulators are built.
You can see a complete list of targets by running `make help` in the
build directory, then e.g.

```
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDOWNLOAD_GMP=TRUE
$ make -C build riscv_sim_rv64f
```

By default `build_simulators.sh` will download and build [libgmp](https://gmplib.org/).
To use a system installation of libgmp, run `env DOWNLOAD_GMP=FALSE ./build_simulators.sh` instead.

Expand All @@ -50,7 +41,7 @@ To use a system installation of libgmp, run `env DOWNLOAD_GMP=FALSE ./build_simu
The simulator can be used to execute small test binaries.

```
$ build/c_emulator/riscv_sim_<arch> <elf-file>
$ build/c_emulator/sail_riscv_sim <elf-file>
```

A suite of RV32 and RV64 test programs derived from the
Expand All @@ -62,13 +53,13 @@ can be run using `make test` or `ctest` in the build directory.

The model is configured using a JSON file specifying various tunable
options. The default configuration used for the model can be examined
using `build/c_emulator/riscv_sim_<arch> --print-default-config`. To
using `build/c_emulator/sail_riscv_sim --print-default-config`. To
use a custom configuration, save the default configuration into a
file, edit it as needed, and pass it to the simulator using the
`--config` option.

Information on other options for the simulator is available from
`build/c_emulator/riscv_sim_<arch> -h`.
`build/c_emulator/sail_riscv_sim -h`.

### Booting OS images

Expand Down
110 changes: 48 additions & 62 deletions c_emulator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,73 +18,59 @@ set(EMULATOR_COMMON_SRCS
config_utils.cpp
)

foreach (xlen IN ITEMS 32 64)
foreach (flen IN ITEMS 32 64)
set(arch "rv${xlen}")
if (flen EQUAL 32)
string(APPEND arch "f")
else()
string(APPEND arch "d")
endif()

add_executable(riscv_sim_${arch}
"${CMAKE_BINARY_DIR}/riscv_model_${arch}.c"
"${CMAKE_BINARY_DIR}/riscv_model_${arch}.h"
${EMULATOR_COMMON_SRCS}
)
# The generated model is not warnings-clean, silence them.
set(_generated_c_warning_opt_out
-Wno-extra
-Wno-unused
-Wno-uninitialized
)
set_source_files_properties("${CMAKE_BINARY_DIR}/riscv_model_${arch}.c"
PROPERTIES COMPILE_OPTIONS "${_generated_c_warning_opt_out}")

if (NOT arch IN_LIST DEFAULT_ARCHITECTURES)
set_target_properties(riscv_sim_${arch} PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()
add_executable(sail_riscv_sim
"${CMAKE_BINARY_DIR}/sail_riscv_model.c"
"${CMAKE_BINARY_DIR}/sail_riscv_model.h"
${EMULATOR_COMMON_SRCS}
)

add_dependencies(riscv_sim_${arch} generated_model_${arch})
# The generated model is not warnings-clean, silence them.
# -Wno-self-assing is needed for `zhtif_tohost = zhtif_tohost`
# generated by the sail code to avoid optimizing the function out.
set(_generated_c_warning_opt_out
-Wno-extra
-Wno-unused
-Wno-uninitialized
$<$<BOOL:${HAVE_WSELF_ASSIGN}>:-Wno-self-assign>
)
set_source_files_properties(
"${CMAKE_BINARY_DIR}/sail_riscv_model.c"
"${CMAKE_BINARY_DIR}/sail_riscv_model.h"
PROPERTIES COMPILE_OPTIONS "${_generated_c_warning_opt_out}"
)

target_link_libraries(riscv_sim_${arch}
PRIVATE softfloat sail_runtime default_config
)
add_dependencies(sail_riscv_sim generated_sail_riscv_model)

target_include_directories(riscv_sim_${arch}
# So the generated C can find riscv_platform/prelude.h"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}"
# So we can find riscv_model_${arch}.h
"${CMAKE_BINARY_DIR}"
)
target_link_libraries(sail_riscv_sim
PRIVATE softfloat sail_runtime default_config
)

# This is necessary so it can pick the right riscv_model_${arch}.h
# to #include. We can remove it when there is a single model with
# runtime xlen and flen.
target_compile_definitions(riscv_sim_${arch}
PRIVATE "RISCV_MODEL_HEADER=<riscv_model_${arch}.h>"
)
target_include_directories(sail_riscv_sim
PRIVATE
# So the generated C can find riscv_platform/prelude.h"
"${CMAKE_CURRENT_SOURCE_DIR}"
# So `riscv_sail.h` can find `sail_riscv_model.h`.
"${CMAKE_BINARY_DIR}"
)

# Enable Sail coverage collection.
if (COVERAGE)
target_compile_definitions(riscv_sim_${arch}
PRIVATE "SAILCOV"
)
endif()
# TODO: Enable warnings when we use the #include trick
# to include the generated Sail code. Currently it
# generates too many warnings to turn these on globally.

# TODO: Enable warnings when we use the #include trick
# to include the generated Sail code. Currently it
# generates too many warnings to turn these on globally.
# target_compile_options(sail_riscv_sim PRIVATE
# -Wall -Wextra
# # Too annoying at the moment.
# -Wno-unused-parameter
# )

# target_compile_options(riscv_sim_${arch} PRIVATE
# -Wall -Wextra
# # Too annoying at the moment.
# -Wno-unused-parameter
# )
# Enable Sail coverage collection.
if (COVERAGE)
target_compile_definitions(sail_riscv_sim
PRIVATE "SAILCOV"
)
endif()

install(TARGETS riscv_sim_${arch}
OPTIONAL
RUNTIME DESTINATION "bin"
)
endforeach()
endforeach()
install(TARGETS sail_riscv_sim
OPTIONAL
RUNTIME DESTINATION "bin"
)
30 changes: 15 additions & 15 deletions c_emulator/riscv_callbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ void print_lbits_hex(lbits val, int length = 0)

// Implementations of default callbacks for trace printing and RVFI.
// The model assumes that these functions do not change the state of the model.
unit mem_write_callback(const char *type, uint64_t paddr, uint64_t width,
unit mem_write_callback(const char *type, sbits paddr, uint64_t width,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, it's a bit sad that Sail doesn't apply the same optimisation despite the explicit constraint on xlen being 32 or 64.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because it needs to know the bit width at runtime now. I think there are some other things you could do but nothing I can think of that's easy and elegant.

It's not too bad though - it knows not to use lbits (GMP) and that's the real performance killers.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to make sure enough primitives have specialised implementations for that type. I think the danger is it will upcast to a GMP-based bitvector to call primitives that don't have optimised implementations for more specific types.

One thing you can do (but not in this PR) is split the configuration in two so you can partially apply it at build time when you really want a specialised implementation for performance.

lbits value)
{
if (config_print_mem_access) {
fprintf(trace_log, "mem[%s,0x%0*" PRIX64 "] <- 0x", type,
static_cast<int>((zphysaddrbits_len + 3) / 4), paddr);
static_cast<int>((zphysaddrbits_len + 3) / 4), paddr.bits);
print_lbits_hex(value, width);
}
if (config_enable_rvfi) {
Expand All @@ -35,12 +35,12 @@ unit mem_write_callback(const char *type, uint64_t paddr, uint64_t width,
return UNIT;
}

unit mem_read_callback(const char *type, uint64_t paddr, uint64_t width,
unit mem_read_callback(const char *type, sbits paddr, uint64_t width,
lbits value)
{
if (config_print_mem_access) {
fprintf(trace_log, "mem[%s,0x%0*" PRIX64 "] -> 0x", type,
static_cast<int>((zphysaddrbits_len + 3) / 4), paddr);
static_cast<int>((zphysaddrbits_len + 3) / 4), paddr.bits);
print_lbits_hex(value, width);
}
if (config_enable_rvfi) {
Expand All @@ -53,7 +53,7 @@ unit mem_read_callback(const char *type, uint64_t paddr, uint64_t width,
return UNIT;
}

unit mem_exception_callback(uint64_t paddr, uint64_t num_of_exception)
unit mem_exception_callback(sbits paddr, uint64_t num_of_exception)
{
(void)num_of_exception;
if (config_enable_rvfi) {
Expand All @@ -63,15 +63,15 @@ unit mem_exception_callback(uint64_t paddr, uint64_t num_of_exception)
}

unit xreg_full_write_callback(const_sail_string abi_name, unsigned reg,
uint64_t value)
sbits value)
{
if (config_print_reg) {
if (config_use_abi_names) {
fprintf(trace_log, "%s <- 0x%0*" PRIX64 "\n", abi_name,
static_cast<int>(zxlen / 4), value);
static_cast<int>(zxlen / 4), value.bits);
} else {
fprintf(trace_log, "x%d <- 0x%0*" PRIX64 "\n", reg,
static_cast<int>(zxlen / 4), value);
static_cast<int>(zxlen / 4), value.bits);
}
}
if (config_enable_rvfi) {
Expand All @@ -80,34 +80,34 @@ unit xreg_full_write_callback(const_sail_string abi_name, unsigned reg,
return UNIT;
}

unit freg_write_callback(unsigned reg, uint64_t value)
unit freg_write_callback(unsigned reg, sbits value)
{
// TODO: will only print bits; should we print in floating point format?
if (config_print_reg) {
// TODO: Might need to change from PRIX64 to PRIX128 once the "Q" extension
// is supported
fprintf(trace_log, "f%d <- 0x%0*" PRIX64 "\n", reg,
static_cast<int>(zflen / 4), value);
static_cast<int>(zflen / 4), value.bits);
}
return UNIT;
}

unit csr_full_write_callback(const_sail_string csr_name, unsigned reg,
uint64_t value)
sbits value)
{
if (config_print_reg) {
fprintf(trace_log, "CSR %s (0x%03X) <- 0x%016" PRIX64 "\n", csr_name, reg,
value);
value.bits);
}
return UNIT;
}

unit csr_full_read_callback(const_sail_string csr_name, unsigned reg,
uint64_t value)
sbits value)
{
if (config_print_reg) {
fprintf(trace_log, "CSR %s (0x%03X) -> 0x%016" PRIX64 "\n", csr_name, reg,
value);
value.bits);
}
return UNIT;
}
Expand All @@ -124,7 +124,7 @@ unit vreg_write_callback(unsigned reg, lbits value)
return UNIT;
}

unit pc_write_callback(uint64_t value)
unit pc_write_callback(sbits value)
{
(void)value;
return UNIT;
Expand Down
24 changes: 12 additions & 12 deletions c_emulator/riscv_callbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@
extern "C" {
#endif

unit mem_write_callback(const char *type, uint64_t paddr, uint64_t width,
unit mem_write_callback(const char *type, sbits paddr, uint64_t width,
lbits value);
unit mem_read_callback(const char *type, uint64_t paddr, uint64_t width,
unit mem_read_callback(const char *type, sbits paddr, uint64_t width,
lbits value);
unit mem_exception_callback(uint64_t paddr, uint64_t num_of_exception);
unit mem_exception_callback(sbits paddr, uint64_t num_of_exception);
unit xreg_full_write_callback(const_sail_string abi_name, unsigned reg,
uint64_t value);
unit freg_write_callback(unsigned reg, uint64_t value);
sbits value);
unit freg_write_callback(unsigned reg, sbits value);
// `full` indicates that the name and index of the CSR are provided.
// 64 bit CSRs use a long_csr_write_callback Sail function that automatically
// makes two csr_full_write_callback calls on RV32.
unit csr_full_write_callback(const_sail_string csr_name, unsigned reg,
uint64_t value);
sbits value);
unit csr_full_read_callback(const_sail_string csr_name, unsigned reg,
uint64_t value);
sbits value);
unit vreg_write_callback(unsigned reg, lbits value);
unit pc_write_callback(uint64_t value);
unit pc_write_callback(sbits value);
unit trap_callback(unit);

// TODO: Move these implementations to C.
unit zrvfi_write(uint64_t paddr, int64_t width, lbits value);
unit zrvfi_read(uint64_t paddr, sail_int width, lbits value);
unit zrvfi_mem_exception(uint64_t paddr);
unit zrvfi_wX(int64_t reg, uint64_t value);
unit zrvfi_write(sbits paddr, int64_t width, lbits value);
unit zrvfi_read(sbits paddr, sail_int width, lbits value);
unit zrvfi_mem_exception(sbits paddr);
unit zrvfi_wX(int64_t reg, sbits value);
unit zrvfi_trap(unit);

#ifdef __cplusplus
Expand Down
8 changes: 4 additions & 4 deletions c_emulator/riscv_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ mach_bits plat_get_16_random_bits(unit)
// either directly in `load_reservation()` or by callling
// `cancel_reservation()`.

unit load_reservation(mach_bits addr)
unit load_reservation(sbits addr)
{
reservation = addr;
reservation = addr.bits;
reservation_valid = true;
RESERVATION_DBG("reservation <- %0" PRIx64 "\n", reservation);
return UNIT;
Expand All @@ -42,10 +42,10 @@ static mach_bits check_mask()
return (zxlen == 32) ? 0x00000000FFFFFFFF : -1;
}

bool match_reservation(mach_bits addr)
bool match_reservation(sbits addr)
{
mach_bits mask = check_mask();
bool ret = reservation_valid && (reservation & mask) == (addr & mask);
bool ret = reservation_valid && (reservation & mask) == (addr.bits & mask);
RESERVATION_DBG("reservation(%c): %0" PRIx64 ", key=%0" PRIx64 ": %s\n",
reservation_valid ? 'v' : 'i', reservation, addr,
ret ? "ok" : "fail");
Expand Down
Loading
Loading