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.
- For Linux use apt package manager. Info under verilator install guide.
- For MacOS the package manager is homebrew.
- For Windows an package manager option is pacman (untested).
- 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)