# Assignment3: SoftCPU
###### tags: `computer architure 2021`
## Introduction
### How Does RISC-V Compliance Tests Works
The RISC-V Architectural Test suite is decribed like a minimal filter in [README](https://github.com/riscv-non-isa/riscv-arch-test/blob/master/doc/README.adoc), and we can find more information here.
> The result that the architecture tests provide to the user is an assurance that the specification has been interpreted correctly and the implementation under test (DUT) can be declared as RISC-V Architecture Test compliant. The result that the architecture tests provide to the user is an assurance that the specification has been interpreted correctly and the implementation under test (DUT) can be declared as RISC-V Architecture Test compliant.
>
According to the RISC-V website, we can find several steps of complliance.

#### Why signature should be matched?
To ensure the processor work properly when executing different types of instructions.
#### How srv32 works with Verilator
The simulator generated by Verilator is called sim. This is a RTL-level generator that is capable of simulating the execution of RISC-V binary at RTL level.
RTL (Register-Transistor-Level) simulation is done on either Verilator (default) or Icarus Verilog.RTL simulator is located in sim/ directory.Result make all command build the core and run RTL sim, all simulation passed.
## Assignment
### Part 1 -- Makefile in sw Directory
#### Rewrite C-Code
Observation:
* Need to add `volatile` before `int` and `char`.
* In the function `printf`, we have to add `\n` at the end of sentence.
```c=
#include <stdio.h>
volatile int lengthOfLastWord(volatile char * s)
{
while(*s != 0)
{
s++;
}
volatile int position = 0;
s--;
while(*s == 32)
{
s--;
}
while(*s != 32)
{
position++;
s--;
}
printf("\nLength of the last word is %d\n", position);
}
int main()
{
volatile char *string = "Hello World";
lengthOfLastWord(string);
return 0;
}
```
#### Process of Compiling in RISC-V
I spent lots of time reading the information of [srv32](https://github.com/sysprog21/srv32) and [kuopinghsu](https://github.com/kuopinghsu/srv32/tree/86bd7f9b7278b17310d2fd4fca44e03b110bc45a). After reading the makefile in srv32 and makefile in dhrystone, we can find that the function of makefile in dhrystone is similar to the function we want for compiling our file. In this way, we can try to modify the makefile and our code to compile.
##### step 1.
Create a folder and put your code, makefile(copy from dhrystone), and syscall.c(copy from dhrystone).

##### step 2.
Modity the makefile.
Change line 4 `SRC = dhry_1.c dhry_2.c syscall.c` to `SRC = filename syscall.c`

##### step 3.
Back to srv32 and do `make LOLW`
```
peishan@peishan-VirtualBox:~/srv32$ make LOLW
```
And it would come out the message and your program result as follow.
```
make[1]: Entering directory '/home/peishan/srv32/sw'
make -C common
make[2]: Entering directory '/home/peishan/srv32/sw/common'
make[2]: Nothing to be done for 'all'.
make[2]: Leaving directory '/home/peishan/srv32/sw/common'
make[2]: Entering directory '/home/peishan/srv32/sw/LOLW'
riscv-none-embed-gcc -O3 -Wall -march=rv32im -mabi=ilp32 -nostartfiles -nostdlib -DTIME -DRISCV -DHZ=100000000 -L../common -Wno-return-type -Wno-implicit-function-declaration -Wno-implicit-int -o LOLW.elf Length_of_Last_Word.c syscall.c -lc -lm -lgcc -lsys -T ../common/default.ld
riscv-none-embed-objcopy -j .text -O binary LOLW.elf imem.bin
riscv-none-embed-objcopy -j .data -O binary LOLW.elf dmem.bin
riscv-none-embed-objcopy -O binary LOLW.elf memory.bin
riscv-none-embed-objdump -d LOLW.elf > LOLW.dis
riscv-none-embed-readelf -a LOLW.elf > LOLW.symbol
make[2]: Leaving directory '/home/peishan/srv32/sw/LOLW'
make[1]: Leaving directory '/home/peishan/srv32/sw'
make[1]: Entering directory '/home/peishan/srv32/sim'
Length of the last word is 5
Excuting 2076 instructions, 2740 cycles, 1.319 CPI
Program terminate
- ../rtl/../testbench/testbench.v:418: Verilog $finish
Simulation statistics
=====================
Simulation time : 0.184 s
Simulation cycles: 2751
Simulation speed : 0.0149511 MHz
make[1]: Leaving directory '/home/peishan/srv32/sim'
make[1]: Entering directory '/home/peishan/srv32/tools'
./rvsim --memsize 128 -l trace.log ../sw/LOLW/LOLW.elf
Length of the last word is 5
Excuting 2076 instructions, 2740 cycles, 1.320 CPI
Program terminate
Simulation statistics
=====================
Simulation time : 0.026 s
Simulation cycles: 2740
Simulation speed : 0.107 MHz
make[1]: Leaving directory '/home/peishan/srv32/tools'
Compare the trace between RTL and ISS simulator
=== Simulation passed ===
```
### Part 2 - Waveform
Open the `wave.fst` in gtkwave.
And we can choose the component we want to see its waveform. Here I insert imem, dmem, clock, pc, branch and some internal instructions.


Look at srv32 architature. We can find the reg and logic signal step by step.


Take the screenshots below for example.
#### clock!

* Clk's period is 4ps.
* After a cycle of clk, pc will increase 4.
* Every 7 cycles of clk, branch turn to 1 for 1 clk.
#### Execution Process!

We can look at the assembly code file in `sw`.

* At the beginning, we can find that the start address is `00000000`, so as imem_addr.
* First line is `imem_addr`, and the number inside means PC.
* `imem_rdata` take the instruction, like the fisrt instruction `00014297 auipc t0 0x14`.
* `0x00014000` is `t0` and it will go down from imm to ex_imm. (IF/ID stage to EX stage)
* And then we can find `0x00014000` went to `wb_result`.
* `0x00014520` is the result from `addi t0, t0, 1440`. And we can see the `ex_result` from EX stage went to WB stage.
### Part 3 - Optimizations
I try another way to complete this problem in less instructions. I reduce two while loop and make there is only on for loop leaved.
#### Code Rewrite
``` c
#include <stdio.h>
#include <string.h>
volatile int lengthOfLastWord(volatile char * s)
{
volatile int len = strlen(s);
volatile int count = 0;
for(volatile int i = len -1; i >=0; i--)
{
if(*(s+i) != ' ')
{
count++;
}
else
break;
}
printf("\nLength of the last word is %d\n", count);
}
int main()
{
volatile char *string = "Hello World";
lengthOfLastWord(string);
return 0;
}
```
After compiling we can see the message as follow,
```
Length of the last word is 5
Excuting 2095 instructions, 2749 cycles, 1.312 CPI
Program terminate
- ../rtl/../testbench/testbench.v:418: Verilog $finish
Simulation statistics
=====================
Simulation time : 0.087 s
Simulation cycles: 2760
Simulation speed : 0.0317241 MHz
make[1]: Leaving directory '/home/peishan/srv32/sim'
make[1]: Entering directory '/home/peishan/srv32/tools'
./rvsim --memsize 128 -l trace.log ../sw/LOLW/LOLW.elf
```
But the result is not as good as I think. I consider that the problem is `if` and `else`. Howerver, I did try other code.
#### Code Rewrite - 2
```c=
#include <stdio.h>
#include <string.h>
volatile int lengthOfLastWord(volatile char * s)
{
volatile int len = strlen(s) - 1;
volatile int count = 0;
while(len >= 0 && s[len] == ' ') --len;
while(len >= 0 && s[len] != ' '){
--len;
++count;
}
printf("\nLength of the last word is %d\n", count);
}
int main()
{
volatile char *string = "Hello World";
lengthOfLastWord(string);
return 0;
}
```
And the result os as follow,
```
Length of the last word is 5
Excuting 2100 instructions, 2756 cycles, 1.312 CPI
Program terminate
Simulation statistics
=====================
Simulation time : 0.010 s
Simulation cycles: 2756
Simulation speed : 0.278 MHz
```
It also didn't do better than original code.
I have been tried in several way to modify the C code but there came out nothing better. If I can out better thought, I will keep modify the assighment.
### Reference
1. [ReadME](https://github.com/riscv-non-isa/riscv-arch-test/blob/master/doc/README.adoc)
2. [Length Of Last Word](https://leetcode.com/problems/length-of-last-word/)
3. [RISCV - International](https://riscv.org/)