How to use
Dependencies
You will need :
A java JDK
SBT (Scala build tool)
Verilator (optional, for simulations)
RVLS / Spike dependencies (optional, if you want to have lock-step simulations checking)
GCC for RISC-V (optional, if you want to compile some code)
On debian :
# JAVA JDK
sudo add-apt-repository -y ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install openjdk-19-jdk -y # You don't exactly need that version
sudo update-alternatives --config java
sudo update-alternatives --config javac
# Install SBT - https://www.scala-sbt.org/
echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list
echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list
curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo apt-key add
sudo apt-get update
sudo apt-get install sbt
# Verilator (optional, for simulations)
sudo apt-get install git make autoconf g++ flex bison
git clone http://git.veripool.org/git/verilator # Only first time
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
unset VERILATOR_ROOT # For bash
cd verilator
git pull # Make sure we're up-to-date
git checkout v4.216 # You don't exactly need that version
autoconf # Create ./configure script
./configure
make
sudo make install
# RVLS / Spike dependencies (optional, for simulations)
sudo apt-get install device-tree-compiler libboost-all-dev
# Install ELFIO, used to load elf file in the sim
git clone https://github.com/serge1/ELFIO.git
cd ELFIO
git checkout d251da09a07dff40af0b63b8f6c8ae71d2d1938d # Avoid C++17
sudo cp -R elfio /usr/include
cd .. && rm -rf ELFIO
# Getting a RISC-V toolchain (optional, if you want to compile RISC-V software)
version=riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14
wget -O riscv64-unknown-elf-gcc.tar.gz riscv https://static.dev.sifive.com/dev-tools/$version.tar.gz
tar -xzvf riscv64-unknown-elf-gcc.tar.gz
sudo mv $version /opt/riscv
echo 'export PATH=/opt/riscv/bin:$PATH' >> ~/.bashrc
Repo setup
After installing the dependencies (see above) :
git clone --recursive https://github.com/SpinalHDL/VexiiRiscv.git
cd VexiiRiscv
# (optional) Compile riscv-isa-sim (spike), used as a golden model during the sim to check the dut behaviour (lock-step)
cd ext/riscv-isa-sim
mkdir build
cd build
../configure --prefix=$RISCV --enable-commitlog --without-boost --without-boost-asio --without-boost-regex
make -j$(nproc)
cd ../../..
# (optional) Compile RVLS, (need riscv-isa-sim (spike)
cd ext/rvls
make -j$(nproc)
cd ../..
Generate verilog
sbt "Test/runMain vexiiriscv.Generate"
You can get a list of the supported parameters via :
sbt "Test/runMain vexiiriscv.Generate --help"
--help prints this usage text
--xlen <value>
--decoders <value>
--lanes <value>
--relaxed-branch
--relaxed-shift
--relaxed-src
--with-mul
--with-div
--with-rva
--with-rvc
--with-supervisor
...
Here is a list of the important parameters :
Parameter |
Description |
---|---|
–xlen=32/64 |
Specify the CPU data width (RISC-V XLEN). 32 bits by default, can be set to 64 bits |
–with-rvm |
Enable RISC-V mul/div instruction |
–with-rvc |
Enable RISC-V compressed instruction set |
–with-rva |
Enable atomic instruction support |
–with-rvf |
Enable 32 bits floating point support |
–with-rvd |
Enable 32/64 bits floating point support |
–with-supervisor |
Enable privileged supervisor, user and MMU |
–allow-bypass-from=Int |
Specify from which execute stage the integer result bypassing is allowed. Default disabled. For performance set it to 0 |
–with-btb |
Enable Branch Target Buffer prediction |
–with-gshare |
Enable GShare conditional branch prediction. (Require the BTB to be enabled) |
–with-ras |
Enable Return Address Stack prediction. (Require the BTB to be enabled) |
–regfile-async |
The register read ports become asynchronous, shaving one stage in the pipeline, but not all FPGA support this kind of memories. |
–mmu-sync-read |
The MMU TLB memories will be implemented using memories with synchronous read ports. This allows to keep it small on FPGA which doesn’t support asynchronous read ports |
–fetch-l1 |
Enable the L1 instruction cache |
–fetch-l1-ways=Int |
Set the number of instruction cache ways (4KB per way by default) |
–lsu-l1 |
Enable the L1 data cache |
–lsu-l1-ways=Int |
Set the number of data cache ways (4KB per way by default) |
–with-jtag-tap |
Enable the RISC-V JTAG debugging. |
There is a lot more parameters which can be turned.
Run a simulation
Important
If you take a VexiiRiscv core and use it with a simulator which does x-prop (not verilator), you will need to add the following option : –with-boot-mem-init. By default this isn’t enabled, as it can degrade timings and area while not being necessary for a fully functional hardware.
Here is how you can run a Verilator based simulation, note that Vexiiriscv use mostly an opt-in configuration. So, most performance related configuration are disabled by default.
sbt
compile
Test/runMain vexiiriscv.tester.TestBench --with-mul --with-div --load-elf ext/NaxSoftware/baremetal/dhrystone/build/rv32ima/dhrystone.elf --trace-all
This will generate a simWorkspace/VexiiRiscv/test folder which contains :
test.fst : A wave file which can be open with gtkwave. It shows all the CPU signals
konata.log : A wave file which can be open with https://github.com/shioyadan/Konata, it shows the pipeline behavior of the CPU
spike.log : The execution logs of Spike (golden model)
tracer.log : The execution logs of VexRiscv (Simulation model)
Here is an example of the additional argument you can use to improve the IPC :
--with-btb --with-gshare --with-ras --decoders 2 --lanes 2 --with-aligner-buffer --with-dispatcher-buffer --with-late-alu --regfile-async --allow-bypass-from 0 --div-radix 4
Here is a screen shot of a cache-less VexiiRiscv booting linux :
Synthesis
VexiiRiscv is designed in a way which should make it easy to deploy on all FPGA. including the ones without support for asynchronous memory read (LUT ram / distributed ram / MLAB). The one exception is the MMU, but if configured to only read the memory on cycle 0 (no tag hit), then the synthesis tool should be capable of inferring that asynchronous read into a synchronous one (RAM block, work on Efinix FPGA)
By default SpinalHDL will generate memories in a Verilog/VHDL synthetisable way. Otherwise, for ASIC, you likely want to enable the automatic memory blackboxing, which will instead replace all memories defined in the design by a consistent blackbox module/component, the user having then to provide those blackbox implementation.
Currently all memories used are “simple dual port ram”. While this is the best for FPGA usages, on ASIC maybe some of those could be redesigned to be single port rams instead (todo).
Other resources
There a few other ways to start using VexiiRiscv :
Using IntelliJ IDEA
IntelliJ IDEA is a Java/Scala IDE which can help a lot navigating the codebase. You can get its community edition for free. Then you just need to install the scala plugin (asked the first time you run the IDE), and open the VexiiRiscv folder with it.
The one issue is that it has a bug, and will give you a :
object Info is not a member of package spinal.core
The workaround is that you need to run the “sbt compile” command in a terminal in the VexiiRiscv folder once.