✔️Port of RISCOF to check the NEORV32 for RISC-V ISA compatibility.
['ghdl', 'isa', 'neorv32', 'processor', 'risc-v', 'riscof', 'riscv', 'sail-riscv', 'verification', 'vhdl']
✔️Port of RISCOF to check the NEORV32 for RISC-V ISA compatibility.
 NEORV32 Core Verification using RISCOF
1. [Prerequisites](#prerequisites)
2. [Setup Configuration](#setup-configuration)
3. [Device-Under-Test (DUT)](#device-under-test-dut)
This repository is a port of the "**RISCOF** RISC-V Architectural Test Framework" to test the
[NEORV32 RISC-V Processor](https://github.com/stnolting/neorv32) for compatibility to the RISC-V
user and privileged ISA specifications. **Sail RISC-V** is used as reference model.
Currently, the following tests are supported:
- [x] `rv32i_m\B` - bit-manipulation (`Zba` + `Zbb` + `Zbs`)
- [x] `rv32i_m\C` - compressed instructions
- [x] `rv32i_m\I` - base integer ISA
- [x] `rv32i_m\M` - hardware integer multiplication and division
- [x] `rv32i_m\privilege` - privileged machine-mode architecture
- [x] `rv32i_m\Zicond` - conditional operations
- [x] `rv32i_m\Zifencei` - instruction stream synchronization
> [!TIP]
> The general structure of this repository was setup according to the
[RISCOF installation guide](https://riscof.readthedocs.io/en/stable/installation.html).
## Prerequisites
Several tools and submodules are required to run this port of the architecture test framework. The repository's
GitHub [Actions workflow](https://github.com/stnolting/neorv32-riscof/blob/main/.github/workflows/main.yml)
takes care of installing all the required packages.
* [neorv32](https://github.com/stnolting/neorv32) submodule - the device under test (DUT)
* [riscv-arch-test](https://github.com/riscv-non-isa/riscv-arch-test) submodule - architecture test cases
* [RISC-V GCC toolchain](https://github.com/stnolting/riscv-gcc-prebuilt) - for compiling native `rv32` code
* [Sail RISC-V](https://github.com/riscv/sail-riscv) - the reference model (a pre-built binary can be found in
the [`bin`](https://github.com/stnolting/neorv32-riscof/tree/main/bin) folder)
* [RISCOF](https://github.com/riscv-software-src/riscof) - the architecture test framework (including
[riscv-isac](https://github.com/riscv-software-src/riscv-isac) and [riscv-config](https://github.com/riscv-software-src/riscv-config))
* [GHDL](https://github.com/ghdl/ghdl) - the _awesome_ VHDL simulator for simulating the DUT
The framework (running all tests) is invoked via a single shell script
[`run.sh`](https://github.com/stnolting/neorv32-riscof/blob/main/run.sh) that returns 0 if all tests were executed
successfully or 1 if there were any errors. The exit code of this script is used to determine the overall success
of the GitHub Actions workflow.
[[back to top](#neorv32-core-verification-using-riscof)]
## Setup Configuration
The RISCOF [config.ini](https://github.com/stnolting/neorv32-riscof/blob/main/config.ini) file 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, ISA spec. versions and
platform modules (like MTIME), are defined by `YAML` files in the according plugin folders.
* DUT: `neorv32` in [`plugin-neorv32`](https://github.com/stnolting/neorv32-riscof/tree/main/plugin-neorv32)
* REF: `sail_cSim` in [`plugin-sail_cSim`](https://github.com/stnolting/neorv32-riscof/tree/main/plugin-sail_cSim)
Each plugin folder also provides low-level _environment_ files like linker scripts (to generate an executable
image matching the target's memory layout) as well as platform-specific code (for example to initialize the target
and to dump the final test signatures/results).
The official [RISC-V architecture tests](https://github.com/riscv-non-isa/riscv-arch-test) repository provides the
individual test cases for all (ratified) RISC-V ISA extensions (user and privilege ISA) that are currently supported
by the DUT. Each test case checks a single instruction or core feature and is compiled into a plugin-specific
executable using a [prebuilt RISC-V GCC toolchain](https://github.com/stnolting/riscv-gcc-prebuilt).
The "golden reference" data is generated by the **Sail RISC-V Model**. This data is compared against the results of
the DUT. The final test report is made available as CSS-flavored HTML file via the
[GitHib actions artifact](https://github.com/stnolting/neorv32-riscof/actions).
> [!TIP]
> Prebuilt _sail-riscv_ binaries for 64-bit x86 Linux are available in the
[`bin`](https://github.com/stnolting/neorv32-riscof/tree/main/bin) folder.
[[back to top](#neorv32-core-verification-using-riscof)]
## Device-Under-Test (DUT)
The [`sim`](https://github.com/stnolting/neorv32-riscof/tree/main/sim) folder provides a plain-VHDL 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 _memory image_ containing
the actual executable. This file generated from a test-case-specific ELF file. The makefile in the `sim` folder
takes care of compilation and will also convert the final memory image into a plain HEX file. Note that this makefile
uses the default software framework from the NEORV32 submodule.
The testbench implements a CPU-external memory module that get initialized with the actual memory image generated by the
test framework. This memory is attached to the processor via its external Wishbone bus interface and is mapped to the core's
reset address at `0x00000000`. The memory is implemented as 4 individual byte-wide memories each providing 1/4 of the total
memory size. This "splitting" is required as GHDL has problems handling large objects (see https://github.com/ghdl/ghdl/issues/1592).
The final test result data (= test signature) is written to a file (`DUT-neorv32.signature`) by the testbench via
consecutive write accesses to address `0xF0000004`. The testbench also provides several memory-mapped "triggers" (at
address `0xF0000000`) to quit the current simulation using VHDL08's `finish` statement or to trigger CPU-external
RISC-V machine-level interrupts (MTI: machine timer interrupt; MEI: machine external interrupt; MSI: machine software
interrupt).
The simulation scripts and the makefile for generating the memory initialization file are invoked and orchestrated from
a DUT-specific Python script in the DUT's plugin folder
(-> [`plugin-neorv32/riscof_neorv32.py`](https://github.com/stnolting/neorv32-riscof/blob/main/plugin-neorv32/riscof_neorv32.py)).
This Python script makes extensive use of shell commands to move and execute files and scripts.
> [!WARNING]
> The Python scripts of **both plugins** override the default `SET_REL_TVAL_MSK` macro from
`riscv-arch-test/riscv-test-suite/env/arch_test.h` to remove the BREAK exception cause from the relocation list as the
NEORV32 sets `mtval` to zero for this type of exception. This is **explicitly permitted** by the RISC-V priv. spec.
[[back to top](#neorv32-core-verification-using-riscof)]