# Assignment3: SoftCPU
contributed by < `handsomehsia` >
## Requirements
:::spoiler
1. Following the instructions of [Lab3: Reindeer - RISCV RV32I[M] Soft CPU](https://hackmd.io/@sysprog/rJw2A5DqS), you shall modify the assembly programs used/done with [Assignment1](https://hackmd.io/@sysprog/2020-arch-homework1) or [Assignment2](https://hackmd.io/@sysprog/2020-arch-homework2) as new test case(s) for [Reindeer](https://github.com/PulseRain/Reindeer) Simulation with Verilator.
* [ I-ADD-01](https://github.com/riscv/riscv-compliance/blob/master/riscv-test-suite/rv32i/src/I-ADD-01.S) is a good starting point for writing test cases.
* You have to ensure signature matched with the requirements described in [RISC-V Compliance Tests](https://github.com/riscv/riscv-compliance/blob/master/doc/README.adoc).
2. Check the generated VCD file and use GTKwave to view the waveform. Then, explain how your program is executed along with [Reindeer](https://github.com/PulseRain/Reindeer) Simulation.
3. Write down your thoughts and progress in [HackMD notes](https://hackmd.io/s/features).
* Summarize how [RISC-V Compliance Tests](https://github.com/riscv/riscv-compliance/blob/master/doc/README.adoc) works and why the signature should be matched.
* Explain how [Reindeer](https://github.com/PulseRain/Reindeer) works with [Verilator](https://www.veripool.org/wiki/verilator).
* What is 2 x 2 Pipeline? How can we benefit from such pipeline design?
* What is "Hold and Load"? And, how the simulation does for bootstraping?
* Can you show some signals/events inside [Reindeer](https://github.com/PulseRain/Reindeer) and describe?
:::
## Modified the assembly code of new test case
### Code from [Assignment1](https://hackmd.io/YVHNqq4vTU-_9E8lWq4FDw?viewhttps://hackmd.io/@sysprog/2020-arch-homework2)
`MIT.s`
```cpp=
.data
length: .word 8 #legs size
star: .string "*"
spa: .string " "
newline: .string "\n"
.text
#s2 = i
#s3 = j
#s4 = legs size
#t0,t1,t2 = temp size
main:
add s2, x0, x0 # i =0
add s3, x0, x0 # j = 0
lw s4, length # size = 8
loop1:
la a0, newline #print newline
li a7, 4
ecall
addi s2, s2, 1 #i = i+1
addi t0, s4, 1 #t0 = size+1
blt s2, t0, cond #for(i < size+1)
j exit
loop2:
addi s3, s3, 1 #j = j+1
add t2, s4, s2 #t0=size+i
bge s3, t2, loop1 #for(j < size+i )
sub t1, s4, s2 #t1=size-i
addi t1, t1, 1 #t1=size-i+1
blt s3, t1, printSpace #if (j < size - i+1)
j printStar
cond:
add s3, x0, x0 # init j
j loop2
printStar:
la a0, star
li a7, 4
ecall
j loop2
printSpace:
la a0, spa
li a7, 4
ecall
j loop2
exit:
li a7, 10
ecall
```
Rewrite the code:
`MIT-01.S`
```cpp=
RVTEST_IO_INIT
RVTEST_IO_ASSERT_GPR_EQ(x31, x0, 0x00000000)
RVTEST_IO_WRITE_STR(x31, "# Test Begin\n")
RVTEST_IO_WRITE_STR(x31, "# Test part 1\n");
la x1, test_A1_1_data
la x2, test_A1_2_data
la x3, test_A1_res
# Load testdata
lw x8, 0(x1)
lw x9, 0(x2)
sw x8, 0(x3)
sw x9, 4(x3)
beqz x9, print
beqz x8, assign
# use Euclidean algorithm
main:
add s2, x0, x0 # i =0
add s3, x0, x0 # j = 0
lw s4, length # size = 8
loop1:
la a0, newline #print newline
li a7, 4
ecall
addi s2, s2, 1 #i = i+1
addi t0, s4, 1 #t0 = size+1
blt s2, t0, cond #for(i < size+1)
j exit
loop2:
addi s3, s3, 1 #j = j+1
add t2, s4, s2 #t0=size+i
bge s3, t2, loop1 #for(j < size+i )
sub t1, s4, s2 #t1=size-i
addi t1, t1, 1 #t1=size-i+1
blt s3, t1, printSpace #if (j < size - i+1)
j printStar
cond:
add s3, x0, x0 # init j
j loop2
printStar:
la a0, star
li a7, 4
ecall
j loop2
printSpace:
la a0, spa
li a7, 4
ecall
j loop2
# ---- ----
RVTEST_IO_CHECK()
RVTEST_IO_ASSERT_GPR_EQ(x3, x8, 0x00000006)
RVTEST_IO_WRITE_STR(x31, "# Test End\n");
RV_COMPLIANCE_HALT
# ---- ----
RV_COMPLIANCE_CODE_END
# Input data section.
.data
test_A1_1_data:
.word 0x0000004E
test_A1_2_data:
.word 0x00000078
#Output data section.
RV_COMPLIANCE_DATA_BEGIN
test_A1_res:
.fill 3, 4, -1
RV_COMPLIANCE_DATA_END
```
## Run with Reindeer
TBD...
## GTKwave
TBD...
## How RISC-V Compliance Tests works
First, by document,
>The RISC-V Compliance Testing framework which is used to test a RISC-V device’s compliance to the different RISC-V specifications.
The goal is to
>Check whether the processor under development meets the open RISC-V standards or not.
So, the `Compliance Test` running the RISC-V code on the processor(soft CPU) and that provides results in ==a defined memory area== (the signature).
Checking if result_signature are matching with the reference_signature.
### Signature
The test signature is defined as reference data written into memory during the execution of the test. It should record values and results of the operation of the Test. It is expected that an implementation, at the end of a test, dumps the signature in to a file such that only 4-bytes are written per line, starting with the most-significant byte on the left.
## How Reindeer works with Verilator
==A C++ testbench(`tb_PulseRain_RV2T.cpp`) will replace the OCD.==
1. The testbench will invoke the toolchain (objdump, readelf) to extract code/data from sections of the .elf file.
2. The testbench will mimic the OCD bus to load the code/data into CPU's memory.
And Verilator will converts Verilog from Reindeer to a model in C++ or SystemC.
## 2x2 pipeline design
Reindeer's pipeline is composed of 4 stages:
1. IF-Instruction Fetch
2. ID-Instruction Decode
3. IE-Instruction Execution
4. WB/MEM-Register Write Back and Memory Access
And 2 x 2 will like this.

(cite from [Reindeer](https://github.com/PulseRain/Reindeer))
In this way, IF and MEM always happen on different clock cycles, thus to avoid the structural hazard caused by the single port memory. Also, because using single port memory, the Reindeer soft CPU is quite portable and flexible across all FPGA platforms.
## Hold and Load
From [Reindeer/README](https://github.com/PulseRain/Reindeer)
>To break the status quo, the PulseRain Reindeer takes a different approach called “hold and load”, which brings a hardware based OCD (on-chip debugger) into the fore, as illustrated below:

So, the `Hold and Load` is for Reindeer to initialize the soft CPU state.
1. When soft CPU be put into a hold state, the memory can be accessd, and new software image can be loaded in.(let OCD to reconfigure the mux and switch the UART TX to OCD side)
2. After loaded, the OCD send singal to active the soft CPU.(the OCD can switch the UART TX back to the CPU side for CPU's output)
### Bootstrapping
The traditional approach
1. Making a boot loader in software
2. Store the boot loader in a ROM
3. After power on reset, the boot loader is supposed to be executed first, for which it will move the rest of the code/data into RAM. And the PC will be setted.