2026-06-09 08:33:18 +02:00

8.9 KiB

RISC-V Desgin in an Open_Source Design Flow

A RISC-V design course, where you learn about the the RISC-V theory and then use it to build your own RISC-V processor.

This project has the full solution for the course exercises. It is able to simulate the Decoder or CPU as DUTs (device under tests). The simulation executes a SW test from a designated Hex file on the DUT.

In this README you will learn about ...

  • what prerequisites you need to get this project running
  • how the project is organised
  • how to run simulations

Background

This course is a part of the initiative Bavarian Chip Design Center (BCDC), which aims to develop and improve the IC-Design workforce in Bavaria.

This course is an adaptation of the code-base developed at Lund University by Per Andersson as part of the effort to develop and improve education in Digital System Design. You can find the original source-code under this repository.

Prerequisites

Bash Shell Terminal

  • Windows: you need to install gitbash or use something similar
  • MacOS/Linux: bash is preinstalled

GCC Compiler

  • Installation details here

Verilator

  • If not installed, you can find the installation here. It is highly recommended to install through a package manager for all operating systems.
  • Export the VERILATOR environment variable. Make sure to use your Verilator install directory -> export VERILATOR=</path/to/verilator>/bin/verilator

Surfer Waveform Viewer

  • If not installed, you can find the installation here
  • Export the WAVE_VIEWER environment variables. Make sure to use your Surfer install directory -> export WAVE_VIEWER=</path/to/surfer>/surfer

IDE (optional)

  • Any IDE of your choosing will make it easy to view and manage the files

Project Organisation

Resources

Path: doc

  • Module diagrams
  • RISC-V Cards
  • RISC-V ISM
  • RISC-V ASM

RTL source code

Path: hw/rtl

File lists of SV code

Path: hw/file_lists

  • RTL file list
  • Testbench file list

Verification source code

SV testbenches used in EDA playground

Path: hw/dv/rtl

Verilator environment

Path: hw/dv/verilator/

  • SV Toplevel harnesses
    • Decoder
    • CPU
  • C++ testbenches
    • Decoder
    • CPU
  • Makefile

Software and Hex programs used for testing

Path: sw/risc-v

  • Main Memory: Hex code
  • Decoder:
    • Assembly code
    • Hex code from assembeled assembly code
  • Fibonacci Series/Hello World/Prime Factors:
    • C Code
    • Assembly code from compiled C code
    • Simplified assembly code from assembly code (not available for Fibonacci Series)
    • Hex code from assembeled assembly or simplified assembly code

Running Simulations

Quick start

Run the two commands below

cd hw/dv/verilator
make clean all run wave

This will convert the RTL into C++, compile the model, create the simulation binary, run the simulation, and start the waveform viewer. The output of the simulation should be the following:

*** Running simulation...

Creating model...
Registered DPI-C functions:
  scopesDump:
    SCOPE 0x55616d28b940: TOP.cpu_harness
       DPI-EXPORT 0x556136cc8f77: enable
       DPI-EXPORT 0x556136cc8f89: getLed
       DPI-EXPORT 0x556136cc8fda: getMem
       DPI-EXPORT 0x556136cc9006: getReg
       DPI-EXPORT 0x556136cc9242: loadRAM
       DPI-EXPORT 0x556136cdcc19: printMem
       DPI-EXPORT 0x556136cdcd88: printReg
       DPI-EXPORT 0x556136cc8fab: setBtn
       DPI-EXPORT 0x556136cc8f68: setClk
       DPI-EXPORT 0x556136cc8f78: setInitial
       DPI-EXPORT 0x556136cc8fea: setMem
       DPI-EXPORT 0x556136cc8fc9: setReset

Initializing SRAM memory from ../../../sw/risc-v/hello_world/helloWorld.hex
program set to helloWorld

Starting model...

Resetting...
Starting CPU...
Running for 500 clock cycles...

Printing evaluation for helloWorld program!

T=3960:                Hex          ASCII
Value at RAM[1024]: 0x68000000 : h
Value at RAM[1025]: 0x00000000 :
Value at RAM[1026]: 0x00000000 :
Value at RAM[1027]: 0x00000000 :


T=6840:                Hex          ASCII
Value at RAM[1024]: 0x68650000 : he
Value at RAM[1025]: 0x00000000 :
Value at RAM[1026]: 0x00000000 :
Value at RAM[1027]: 0x00000000 :


T=9720:                Hex          ASCII
Value at RAM[1024]: 0x68656c00 : hel
Value at RAM[1025]: 0x00000000 :
Value at RAM[1026]: 0x00000000 :
Value at RAM[1027]: 0x00000000 :


T=12600:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x00000000 :
Value at RAM[1026]: 0x00000000 :
Value at RAM[1027]: 0x00000000 :


T=15480:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x6f000000 : o
Value at RAM[1026]: 0x00000000 :
Value at RAM[1027]: 0x00000000 :


T=18360:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x6f200000 : o
Value at RAM[1026]: 0x00000000 :
Value at RAM[1027]: 0x00000000 :


T=21240:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x6f207700 : o w
Value at RAM[1026]: 0x00000000 :
Value at RAM[1027]: 0x00000000 :


T=24120:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x6f20776f : o wo
Value at RAM[1026]: 0x00000000 :
Value at RAM[1027]: 0x00000000 :


T=27000:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x6f20776f : o wo
Value at RAM[1026]: 0x72000000 : r
Value at RAM[1027]: 0x00000000 :


T=29880:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x6f20776f : o wo
Value at RAM[1026]: 0x726c0000 : rl
Value at RAM[1027]: 0x00000000 :


T=32760:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x6f20776f : o wo
Value at RAM[1026]: 0x726c6400 : rld
Value at RAM[1027]: 0x00000000 :


T=35640:               Hex          ASCII
Value at RAM[1024]: 0x68656c6c : hell
Value at RAM[1025]: 0x6f20776f : o wo
Value at RAM[1026]: 0x726c6421 : rld!
Value at RAM[1027]: 0x00000000 :


Done, closing simulation.

Important: The makefile reads the file lists hw/file_lists/rtl_flist.f and hw/file_lists/tb_flist.f. The make process does not recognize RTL or TB file changes, so it is required to run make clean after RTL or TB changes, otherwise the changed files will not be compiled.

Makefile details

By default, the hex-file sw/risc-v/hello_world.hex is loaded into the Main Memory before starting the simulation, and the simulation is run for 500 clock cycles. This behavior can by overriden by specifying PROG and NCYCLES environment variables, which will load the specified hex file and run for the specified number of clock cycles.

make run PROG=/path/to/myprog.hex NCYCLES=2000

You can also combine the different make targets.

make clean all run wave PROG=/path/to/myprog.hex NCYCLES=2000

Important: If the simulation is not showing the full expected result, make sure that the number of cycles are sufficient for the CPU to finish executing the program (check if the last instruction executed is the jump to halt instruction)

Available Make targets (commands)

  • make clean - remove old files
  • make - build and compile the RTL simulator using Verilator and GCC (alias for make all)
  • make run - run the simulation (requires built and compiled RTL)
  • make wave - show the waveform in Surfer
  • make clean all run wave - runs all the make targets (order is important)

It is not necessary to close the Surfer viewer if the simulation is run again. Just reload the Surfer window after the new run.

Things you need/can change

  • In the Makefile
    • PROG - variable holding the hex file path of the program to be loaded
    • NCYCLES - Variable for the number of cycles the CPU is going to run for
    • TOPMODULE - Variable with the module name of the harness
  • Int the tb_flist.f
    • Add new harnesses you have created

Things you can do

  • You can run the exact same tests you ran on EDA Playground and you should get the same results. However, the test here will be using verilator.
  • You can create your own harness and C++ testbenches for modules other than Decoder and CPU to get familiar with verilator testbenches
  • Compare Icarus and Verilator output (uninitialised memory locations are set to 'x' when using Icarus and '0' when using verilator)