Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?


Failed to load latest commit information.
Latest commit message
Commit time

NEORV32 Core Verification using RISCOF

neorv32-riscof License Gitter

  1. Prerequisites
  2. Setup Configuration
  3. Device-Under-Test (DUT)
  4. Compatibility Issues

This repository is a port of the "RISCOF RISC-V Architectural Test Framework" to test the NEORV32 RISC-V Processor for compatibility to the RISC-V user and privileged ISA specifications. The Sail RISC-V model is used as reference model. Currently, the following tests are supported:

  • rv32i_m\B - bit-manipulation (Zba, Zbb, Zbc, Zbs)
  • rv32i_m\C - compressed instructions
  • rv32i_m\I - base integer ISA
  • rv32i_m\M - hardware multiplication and division
  • rv32i_m\privilege - privileged machine architecture
  • rv32i_m\Zifencei - instruction stream synchronization

💡 The general structure of this repository was setup according to the RISCOF installation guide.


Several tools and submodules are required to run this port of the architecture test framework. The repository's GitHub workflow takes care of installing all the required packages.

The framework (running all tests) is invoked via a single shell script that returns 0 if all tests were executed successfully and 1 if there were any errors. The exit code of this script is used to determine the overall success of the GitHub action and thus, defines the workflow's badge status.

[back to top]

Setup Configuration

The RISCOF config.ini is used to configure the plugins to be used: the device-under-test ("DUT") and the reference model ("REF"). The ISA, debug and platform specifications, which define target specific configurations like available ISA extensions, platform modules like MTIME and ISA spec. versions, are defined via YAML files in the DUT's plugin folder.

According to the plugin, each plugin folder also provides low-level environment files like linker scripts (to generate an executable matching the target's memory layout) and platform-specific code (for example to initialize the target and to dump test results).

The official RISC-V architecture tests repository provides test cases for all (ratified) RISC-V ISA extensions (user and privilege ISA). Each test case tests a single instruction or core feature and is compiled into a plugin-specific executable using a prebuilt RISC-V GCC toolchain.

The "golden" reference data is generated using the Sail RISC-V Model and compared to the results of the DUT. The final test report is available as CSS-flavored HTML file via the GitHib actions artifact.

💡 Prebuilt sail-riscv binaries for 64-bit Linux are available in the bin folder.

[back to top]

Device-Under-Test (DUT)

The sim folder provides a simple testbench and shell scripts to simulate the NEORV32 processor using GHDL. The testbench provides generics to configure the DUT's RISC-V ISA extensions and also to pass a plain ASCII HEX file, which represents the actual executable to be executed ("memory initialization file") that is generated by the folder's makefile from a test-specific ELF file.The makefile uses the default software framework from the NEORV32 submodule (more specific: the image generator) to generate a memory initialization file from a compiled ELF file.

⚠️ The testbench implements four CPU-external memory modules that get initialized with the actual executable. The memories are coupled using the processor's Wishbone external bus interface and are mapped to the core's reset address at 0x00000000. Each memory module implements a physical memory size of 512kB resulting in a total memory size of 2MB (the largest test case executable comes from the I/jal test case with approx. 1.7MB). This "splitting" is required as GHDL has problems handling large objects (see ghdl/ghdl#1592).

📚 The "simulation mode" of the processor's UART0 module is used to dump the test result data (= the test signature) to a file. More information regarding the UART simulation mode can be found in the NEORV32 online data sheet.

The testbench also provides a "trigger mechanism" to quit the current simulation using VHDL08's finish statement. Quitting the simulation is triggered by writing 0xCAFECAFE to address 0xF0000000, which is implemented (software) by the DUT's plugin environment module. A maximum simulation timeout of 2ms is provided to terminate faulty simulations that might end up in an infinite loop.

The simulation scripts and the makefile for generating the memory initialization file are invoked from DUT- specific Python script in the DUT's plugin folder (-> plugin-neorv32/ This Python script makes extensive use of shell commands to move and execute files and scripts (my Python skills are still quite limited 😅).

[back to top]

Compatibility Issues

⚠️ ⚠️ ⚠️

The current version of the Sail RISC-V model does not support a target-specific configuration of the core's events that update the mtval trap value CSR: the NEORV32 writes zero to this CSR when encountering an ebreak (breakpoint) exception while the original Sail model writes the address of the triggering ebreak instruction to mtval. However, constraining platform-specific events that write (or not) to mtval is explicitly allowed by the RISC-V ISA specification (see riscv-software-src/riscv-config/issues/16).

To circumvent this, a patch is applied to the default riscv-arch-test submodule, which adds code to set the mtval portion of the test signature to all-zero if a breakpoint exception occurs. This is only relevant for the privilege/ebreak.S and C/cebreak-01.S test cases.

This "hack" might be abandoned with future versions of RISCOF/sail.

[back to top]