/blog/images/avatar.png

CVA6 - Adding a CSR and verifying the behavior in GTKwave

Introduction

The idea of this post is to explore how we can add a CSR in the CVA6 processor and how we can check its behavior in GTKwave. In this tutorial, we will try to add a CSR named dmpcfg at address 0x3f0.

Adding a CSR in the CVA6 HDL code

For the CVA6, CSRs are implemented in https://github.com/openhwgroup/cva6/blob/master/core/csr_regfile.sv. In this file, there are two important processes:

Update of the write logic

https://github.com/openhwgroup/cva6/blob/master/core/csr_regfile.sv#L488

CVA6 - Verilator model and VCD generation

Introduction

CVA6 is a processor from the OpenHW Group: 64-bit, Linux-capable and written in SystemVerilog.

git clone https://github.com/openhwgroup/cva6
cd cva6
git submodule update --init --recursive

Toolchain

Then, you need a toolchain as explained here. Just follow the Prerequisites and Getting started sections in util/toolchain-builder directory.

Simulators

cd <cva6_repo_root>
cd verif/regress
# Install Verilator
./install-verilator.sh
# Install Spike
./install-spike.sh

Standalone simulations

# Toolchain path
$ export RISCV=/opt/riscv_cva6
# Set environment variables
$ source verif/sim/setup-env.sh
# If needed
# export NUM_JOBS=
# Which simulator to be used, Verilator here
$ export DV_SIMULATORS=veri-testharness
# Launch default hello world simulation for cv32a65x variant
$ cd ./verif/sim

Default simulation - Hello world

$ python3 cva6.py --target cv32a65x --iss=$DV_SIMULATORS --iss_yaml=cva6.yaml \
--c_tests ../tests/custom/hello_world/hello_world.c \
--linker=../../config/gen_from_riscv_config/linker/link.ld \
--gcc_opts="-static -mcmodel=medany -fvisibility=hidden -nostdlib \
-nostartfiles -g ../tests/custom/common/syscalls.c \
../tests/custom/common/crt.S -lgcc \
-I../tests/custom/env -I../tests/custom/common"
GCC Version: 13.1.0
Spike Version: 1.1.1-dev 60e57248
Verilator Version: Verilator 5.008 2023-03-04 rev v5.008 (mod)
Creating output directory: /home/user/cva6/verif/sim/out_2025-07-25

Iteration number: 1
Compiling test: ../tests/custom/hello_world/hello_world.c
Compilation cmd: /opt/riscv_cva6/bin/riscv-none-elf-gcc ../tests/custom/hello_world/hello_world.c               -I/home/user/cva6/verif/sim/dv/user_extension                 -T../../config/gen_from_riscv_config/linker/link.ld -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g ../tests/custom/common/syscalls.c ../tests/custom/common/crt.S -lgcc -I../tests/custom/env -I../tests/custom/common -o /home/user/cva6/verif/sim/out_2025-07-25/directed_tests/hello_world.o  -march=rv32imc_zba_zbb_zbs_zbc_zicsr_zifencei -mabi=ilp32
Processing ISS setup file: cva6.yaml
Found matching ISS: veri-testharness
Target: cv32a65x
ISA rv32imc_zba_zbb_zbs_zbc_zicsr_zifencei
/home/user/cva6/verif/sim/out_2025-07-25/directed_tests/hello_world.o
[veri-testharness] Running ISS simulation: make veri-testharness target=cv32a65x variant=rv32imc_zba_zbb_zbs_zbc_zicsr_zifencei elf=/home/user/cva6/verif/sim/out_2025-07-25/directed_tests/hello_world.o path_var=/home/user/cva6/ tool_path=/home/user/cva6/tools/spike/bin isscomp_opts="" issrun_opts="+debug_disable=1 +ntb_random_seed=787586395" isspostrun_opts="0x0000000080000000" log=/home/user/cva6/verif/sim/out_2025-07-25/veri-testharness_sim/hello_world.cv32a65x.log &> /home/user/cva6/verif/sim/out_2025-07-25/veri-testharness_sim/hello_world.cv32a65x.log.iss
[veri-testharness] Running ISS simulation: /home/user/cva6/verif/sim/out_2025-07-25/directed_tests/hello_world.o ...done
Processing ISS setup file: cva6.yaml
Found matching ISS: veri-testharness
Target: cv32a65x
ISA rv32imc_zba_zbb_zbs_zbc_zicsr_zifencei
veri-testharness sim log dir: /home/user/cva6/verif/sim/out_2025-07-25/veri-testharness_sim

Simulation and VCD exploitation

Before launching the Python script, you just need to export TRACE_FAST=1

Rocket RISC-V processor - Dot diagrams

The Rocket Chip is poorly documented. In order to understand the structure of the Scala code, https://github.com/freechipsproject/diagrammer is a tool able to generate Dot diagrams helping to understand what’s inside this processor.

NB: it has been on Debian Bullseye

# Prerequisites, dot interactive viewer
sudo apt install xdot
# Cloning the diagram generation tool
git clone https://github.com/freechipsproject/diagrammer
cd diagrammer
git checkout v1.3.3
./diagram.sh -i freechips.rocketchip.system.DefaultConfig.fir --module-name "Rocket" --just-top-level

Note that the *.fir file is available in the Rocket Chip emulator directory. Arguments:

Rocket RISC-V processor - Debugging a program

The idea is to debug a simple program on an emulated Rocket. The procedure is well defined on the officiel Rocket repository. This post basically summarizes the needed steps.

Generating the emulator

Let’s say you have a default rocket-chip repository. You need to add a Remote Bit-Bang client in the emulator by modifying src/main/scala/system/Configs.scala:

@@ -86,3 +86,4 @@ class MMIOPortOnlyConfig extends Config(
 
 class BaseFPGAConfig extends Config(new BaseConfig ++ new WithCoherentBusTopology)
 class DefaultFPGAConfig extends Config(new WithNSmallCores(1) ++ new BaseFPGAConfig)
+class DefaultConfigRBB extends Config(new WithJtagDTMSystem ++ new WithNBigCores(1) ++ new WithCoherentBusTopology ++ new BaseConfig)

Building the emulator

rocket-chip$ cd emulator
emulator$ CONFIG=freechips.rocketchip.system.DefaultConfigRBB make

Compiling a test program

char text[] = "Vafgehpgvba frgf jnag gb or serr!";

// Don't use the stack, because sp isn't set up.
volatile int wait = 1;

int main()
{
    while (wait)
        ;

    // Doesn't actually go on the stack, because there are lots of GPRs.
    int i = 0;
    while (text[i]) {
        char lower = text[i] | 32;
        if (lower >= 'a' && lower <= 'm')
            text[i] += 13;
        else if (lower > 'm' && lower <= 'z')
            text[i] -= 13;
        i++;
    }

    while (!wait)
        ;
}

This C code (which performs a ROT13 transformation) can be saved in a hello_world.c file. In order to compile it, it is assumed to the same settings as for riscv-tests. In this case, it is assumed we modified this C code and that debug symbols were added in the Makefile.

Playing with Verilator and GTKWave

Verilator basics

Basic steps for a simulation of an ALU in Verilator is given in this link.

GTKWave customization

There are two way to customize the GTKWave rendering :

Command

gtkwave -S tcl_script.tcl waveform.vcd 

GTKWave will look for .gtkwaverc in:

Rocket RISC-V processor - Adding custom instructions

Introduction

In the context of a research project related to RIMI [1], we need to add new instructions in the Rocket Chip decoder. @QDucasse already proposed an analysis of the structure of the Rocket Chip source code [1]. This blog post aims to give a bit more details about the instruction encoding for load/store instructions in particular.

Instructions specification

Each instruction is defined with a dictonary of parameters (rocket-chip/IDecode.scala at v1.6 · chipsalliance/rocket-chip · GitHub). Here is a summary for the RVI subset: