# [RISC-V Desgin in an Open_Source Design Flow](https://microtec-academy.de/produkt/risc-v-design-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)*](https://www.iis.fraunhofer.de/en/ff/sse/bavarian-chip-design-center.html), 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](https://www.lth.se/) 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](https://github.com/PalePrime/single_cycle).

## Prerequisites

Bash Shell Terminal

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

GCC Compiler

* Installation details [here](https://gcc.gnu.org/install/)

Verilator

* If not installed, you can find the installation [here](https://verilator.org/guide/latest/install.html). 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](https://verilator.org/guide/latest/install.html#package-manager-quick-install).
  * For MacOS the package manager is [homebrew](https://brew.sh/).
  * For Windows an package manager option is [pacman](https://gist.github.com/AndreSteenveld/cb6662c93c8323795c5fd347defb8976) (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](https://surfer-project.org/)
* 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)
