owned this note
owned this note
Published
Linked with GitHub
---
title: 'Lab3: Reindeer - Soft CPU'
disqus: hackmd
---
# Lab3: Reindeer - Soft CPU
Suraj Pramanik(潘家瑞)
## Table of Contents
[TOC]
## Setup environment
It took long time to understand the relation between those folder and their work in compiling riscv. I also have analysed the I-ADD-01 file to get better understanding.
I had to modify some files in order to compile testcase file.
Few git file has been cloned in the system. So, the folder, ```/home/surajubuntu/riscv-compliance/riscv-test-suie/rv32i``` is where we will compile our testcase.
Modify the Makefile:
* TARGETDIR := /home/surajubuntu/riscv-compliance/riscv-target
* RISCV_TARGET := riscvOVPsim
* RISCV_DEVICE := rv32i
then move to other file: /home/surajubuntu/riscv-compliance/riscv-target/riscvOVPsim/device/rv32i/Makefile.include
Change TARGET_SIM to this.
```shell
TARGET_SIM ?= /home/surajubuntu/riscv-compliance/riscv-ovpsim/bin/Linux64/riscvOVPsim.exe
```
## Modified assembly code
```cpp
#include "test_macros.h"
#include "compliance_test.h"
#include "compliance_io.h"
RV_COMPLIANCE_RV32M
RV_COMPLIANCE_CODE_BEGIN
RVTEST_IO_INIT
RVTEST_IO_ASSERT_GPR_EQ(x31, x0, 0x00000000)
RVTEST_IO_WRITE_STR(x31, "# Test Begin\n")
#------------------------------------------------
RVTEST_IO_WRITE_STR(x31, "# Reserved registers are ra(x3)")
#Addresses for test data and results
la x1, test_A1_data
la x2, test_A2_data
la x3, test_res
jal x3, pow
j end
pow:
#store the values
addi x6, x6, -8
sw x3, 4(x6)
sw x7, 0(x6)
addi x7, zero, 1
addi x8, zero, 1
bne x2, zero, loop
j end
loop:
mv x9, x1
mv x10, x7
jal x3, multiplyBase
addi x2, x2, -1
bne x2, zero, loop
mv x1, x7
lw x7, 0(x6)
lw x3, 4(x6)
addi x6, x6, 8
j end
multiplyBase:
add x7, x7, x10
addi x9, x9, -1
bne x9, x8, multiplyBase
j end
end:
RVTEST_IO_WRITE_STR(X31, "Test finished\n")
RV_COMPLIANCE_HALT
RV_COMPLIANCE_CODE_END
.data
test_A1_data:
.word 0x00000005
test_A2_data:
.word 0x00000003
RV_COMPLIANCE_DATA_BEGIN
test_res:
.fill 1, 4, -1
RV_COMPLIANCE_DATA_END
```
## Procedure for testing program:
At first, create a .S file in the ```riscv-compliance/riscv-test-suite/rv32i/src``` folder. Then run this file in the /home/surajubuntu/riscv-compliance/riscv-test-suite/rv32i/ folder by typing: make pow.elf (as my file name pow). But before that, we should put pow name inside the Makefrag make file.
```shell
rv32i_sc_tests = \
Test \
pow \
...
```
compile .S file
```shell
riscv-compliance/riscv-test-suite/rv32i$ make pow.elf
riscv-none-embed-gcc -march=rv32i -mabi=ilp32 -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -I/home/surajubuntu/riscv-compliance/riscv-test-env/ -I/home/surajubuntu/riscv-compliance/riscv-test-env/p/ -I/home/surajubuntu/riscv-compliance/riscv-target/riscvOVPsim/ -T/home/surajubuntu/riscv-compliance/riscv-test-env/p/link.ld src/pow.S -o /home/surajubuntu/work/pow.elf; riscv-none-embed-objdump -D /home/surajubuntu/work/pow.elf > /home/surajubuntu/work/pow.elf.objdump
surajubuntu@s
```
After running successfully, two files will generate in the /work folder.
```
work --
|--I-ADD-01.elf
|--I-ADD-01.elf.objdump
|--Test.elf
|--Test.elf.objdump
|--pow.elf
|--pow.elf.objdump
```
Then we could simply copy .elf and .objdump files and paste it in the Reindeer/sim/compliance/ folder.
Then test the testcase by ```make test pow``` command. Then we can see test case passed.
Now we can able to check gtk graph.
```shell
surajubuntu@surajubuntu:~/Reindeer/sim/verilator$ make test pow
====> Testing ./obj_dir/VPulseRain_RV2T_MCU
TEST CASE: pow
testing ../compliance/pow.elf -r ../compliance/references/pow.reference_output
=============================================================
=== PulseRain Technology, RISC-V RV32IM Test Bench
=============================================================
elf file : ../compliance/pow.elf
reference : ../compliance/references/pow.reference_output
start address = 0x80000000
begin signature address = 0x80002010
end signature address = 0x80002020
=============> reset...
=============> init stack ...
=============> load elf file...
Loading section .text.init ... 1c4 bytes, LMA = 0x80000000
80000000 04c0006f
80000004 34202f73
80000008 00800f93
8000000c 03ff0a63
80000010 00900f93
```
## VCD files
After finishing all the procedure, we can generate .vcd file in the ```/Reindeer/sim/verilator/```
Start verilator by ```$ gtkwave pow.vcd```
## GTKWave form

From this picture, we can trace each signal. At specific time point, we could see values of the each signal. For that it is easier to debug where we are doing wrong.
* clk stands for clock cycle
* reset_n is reset the cpu. At the beginning, it sets to 0, then 1.
* mem_read_data[31:0] memory read data, reading data to the memory.
* S_DECODE[31:0] , fist decode data
* S_DECODE_DATA[31:0], second decode data
* and so on....
Qustion-Answer
---
* Explain how Reindeer works with Verilator.

Run the .elf file with the test branch with the ```tb_PulseRain_RV2T.cpp``` code. It will generate all signals and values. Put those values in the memory, then we can compare the signature from the reference output files.
start_address set to 0x80000000
address of the starting signature and ending signature will be generated depends on testcase.
In the cpp file, there has some important functions like-
void uut_memory_load -> which load data into the UUT memory
uut_memory_peek -> read uut memory and match with the reference output file whether match or not.

:::danger
Avoid putting screenshots which only contains the source listing. Instead, use code block in Markdown.
:::
Initialize all the variables and set to 0. then call reset. Then initialize the stack as default.

Load .elf to the uut memory. Then start the cpu, run for TOTAL_RUN_CYCLES = 2000 times. It will generate all the memory peek addresses. Then reset the cpu for the second time and put it into hold state
Then we will compare those values with the reference_output files whethere they have match or not.
* What is “Hold and Load”? And, how the simulation does for bootstraping?

Basically, "Hold and Load" means when the soft cpu put into a hold state just after the reset, a new software image can be loaded into memory through OCD by switching the UART TX. Therefore, the memory can be accessed and the control frame can be exchanged.
This implementation gets software images from an external host, therefore, it doesn't need any ROM to begin with.
Bootstrapping works as follows:
At first, to make a boot loader in software
Then, store the bootloader in a ROM
After that, power on reset, boot loader executed to move the code/data into RAM. Then PC will be set to the _start address of the new image.