Contents

CVA6 - Verilator model and VCD generation

Introduction

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

Requirements

In order to generate a CVA6 model with export, Verilator must be installed from sources as the Makefile requires a C++ file https://github.com/pcotret/cva6/blob/vcd_patch/Makefile#L551. For this tutorial, Verilator 4.110 was chosen as it is used in the official CI flow.

1
2
3
4
5
6
7
8
git clone https://github.com/verilator/verilator
cd verilator
git pull
git checkout v4.110
export VERILATOR_ROOT=`pwd`
autoconf
./configure
make -j$(nproc)

Verilator binaries are generated in <verilator_cloned_repo/bin> and must be added to PATH.

Environment variables

  • export VERILATOR_ROOT=<verilator_cloned_repo>: path for C++ files required to generate a VCD-capable model.
  • export CVA6_REPO_DIR=<cva6_cloned_repo>: CVA6 repository.
  • export RISCV=<riscv_toolchain>: RISC-V toolchain.
  • export PATH=<verilator_cloned_repo>/bin:$PATH: Verilator binaries added to PATH.

Verilator model

It is assumed you have cloned the CVA6 repository and its submodules.

1
make verilate DEBUG=1 TRACE_FAST=1

The model is generated at: work-ver/Variane_testharness. Please note that make verilate will generate a model without VCD if needed.

Default simulation

1
./work-ver/Variane_testharness riscv-tests/isa/rv64ui-p-add

Simulation and VCD exploitation

1
./work-ver/Variane_testharness -v rv64ui-p-add.vcd riscv-tests/isa/rv64ui-p-add

-v specifies the name of the VCD file.

Binaries support

Bare metal binaries generated from https://github.com/riscv-software-src/riscv-tests should work without modifications.

Example of CVA6 and GTKWave on a simple test

1
2
3
cd work-ver
./Variane_testharness -v rv64ui-p-add.vcd riscv-tests/isa/rv64ui-p-add
gtkwave rv64ui-p-add.vcd

Let’s say we want to analyze/check the tests 3 and 4 of rv64ui-p-add.

In GTKWave, we will watch:

  • The instruction address.
  • The instruction value.
  • Register addresses (rs1, rs2 and rd as we check a R-type instruction).
  • Result of the ALU.

Reading the dump of the test, we will look at this part:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
000000008000010c <test_3>:
    8000010c:	00100093          	li	ra,1
    80000110:	00100113          	li	sp,1
    80000114:	00208733          	add	a4,ra,sp
    80000118:	00200393          	li	t2,2
    8000011c:	00300193          	li	gp,3
    80000120:	4c771463          	bne	a4,t2,800005e8 <fail>

0000000080000124 <test_4>:
    80000124:	00300093          	li	ra,3
    80000128:	00700113          	li	sp,7
    8000012c:	00208733          	add	a4,ra,sp
    80000130:	00a00393          	li	t2,10
    80000134:	00400193          	li	gp,4
    80000138:	4a771863          	bne	a4,t2,800005e8 <fail>
1
2
  TEST_RR_OP( 3,  add, 0x00000002, 0x00000001, 0x00000001 );
  TEST_RR_OP( 4,  add, 0x0000000a, 0x00000003, 0x00000007 );

Signals observed in GTKWave

Note: sometimes, it’s helpful to have the Vivado project opened. Much easier to explore the CPU hierarchy

  • TOP.ariane_testharness.i_ariane.i_cva6.id_stage_i.fetch_entry_i.address[63:0]
  • TOP.ariane_testharness.i_ariane.i_cva6.id_stage_i.fetch_entry_i.instruction[31:0]
  • TOP.ariane_testharness.i_ariane.i_cva6.id_stage_i.decoder_i.instr.rtype.rs1[19:15]
  • TOP.ariane_testharness.i_ariane.i_cva6.id_stage_i.decoder_i.instr.rtype.rs2[24:20]
  • TOP.ariane_testharness.i_ariane.i_cva6.id_stage_i.decoder_i.instr.rtype.rd[11:7]
  • TOP.ariane_testharness.i_ariane.i_cva6.ex_stage_i.alu_i.result_o[63:0]

RISC-V register values can be found at: https://en.wikichip.org/wiki/risc-v/registers

Test 3 1+1=2

../img/test3.jpg

  • reg(@1)+reg(@2)=reg(@E)
  • Values: 1 + 1 = 2
  • 895 ps : rs1 operand
  • 897 ps : it should be rs2 operand. However, as it’s equal to rs1, nothing is generated
  • 899 ps : rd operand

Test 4 3+7=0xA

../img/test4.jpg

  • reg(@1)+reg(@2)=reg(@E)
  • Values: 3 + 7 = 0xA
  • 917 ps : rs1 operand
  • 919 ps : rs2 operand
  • 921 ps : rd operand