# Assignment3: single-cycle RISC-V CPU
Contributed by [Yan-You Chen](https://github.com/y0y0alex/ca2023-lab3)
## Prepare
### envirnment:
* use the Ubuntu 22.04 that homework2 already used
### Install the dependent packages
```shell
$ sudo apt install build-essential verilator gtkwave
```
### Install sbt
```
$ sudo apt-get install openjdk-11-jdk
$ curl -fL https://github.com/coursier/coursier/releases/latest/download/cs-x86_64-pc-linux.gz | gzip -d > cs && chmod +x cs && ./cs setup
```
## Learning about Chisel
* Chisel is an domain specific language (DSL) implemented using Scala’s macro features.
* different write type between Chisel and usually C, `if(value == 12)` need to be rewrited `when(value === 12.U)`
* read and learn the example from `/chisel-tutorial/src/main/scala/examples` , there are a lot of example.
* read this web in the provided artical [Learn Chisel online!](https://hackmd.io/@sysprog/r1mlr3I7p#Chisel-Bootcamp)
## Chisel Tutorial
### Getting the Repository
```
$ git clone https://github.com/ucb-bar/chisel-tutorial
$ cd chisel-tutorial
$ git checkout release
$ sbt run
```
* `sbt run` will try to run and you may see the follow message below.
```
[info] Loading project definition from /home/ca/chisel-tutorial/project
[info] Loading settings for project chisel-tutorial from build.sbt ...
[info] Set current project to chisel-tutorial (in build file:/home/ca/chisel-tutorial/)
[info] running hello.Hello
[info] [0.011] Elaborating design...
[info] [0.208] Done elaborating.
Computed transform order in: 405.3 ms
Total FIRRTL Compile Time: 708.5 ms
End of dependency graph
Circuit state created
[info] [0.001] SEED 1700628972323
test Hello Success: 1 tests passed in 6 cycles taking 0.028778 seconds
[info] [0.008] RAN 1 CYCLES PASSED
[success] Total time: 6 s, completed 2023年11月22日 下午12:56:17
```
## Single-cycle RISC-V CPU
### Implementation: Get the repository:
```
$ git clone https://github.com/sysprog21/ca2023-lab3
$ cd ca2023-lab3
```
* This step get the architecture of Single-cycle RISC-V CPU that we need to write.
## Write MyCPU
### Add the code that we need to write in MyCPU
* After I see the files I get, there are not many code we need to take.
* In the dir `ca2023-lab/src/main/scala/riscv/core`, you only need to write
* CPU.scala
* InstructionFetch.scala
* InstructionDecod.scala
* Execute.scala
* The blank that you should add is make by
```
lab3(***) begin
lab3(***) end
```
* That means we only need to select some signal which correct and connect some wire out.
* The detail that each stage is write in [professor provided](https://hackmd.io/@sysprog/r1mlr3I7p#Single-cycle-RISC-V-CPU).
### Some problem I take
* First I try to use the `Muxlookup`, that instruction has already has a different format. Finally I change to use `Mux`.
### The code I write in each
#### InstructionFetch
* `io.jump_flag_id` should be used to decided the `io.jump_address_id`
#### InstructionDecode
* Initially, `io.memory_read_enable` and `io.memory_write_enable` don't determine.
* The opcode should be decided by InstructionTypes
#### Execute
* We should write the `alu.io.op1`, `alu.io.op2 ` and `alu.io.func`.
* We need check the `alu.io.op1` that `aluop1_source.asBool` , `io.instruction_address` or `io.reg1_data` which is source
* `alu.io.op2` is like `alu.io.op1` but need some fixed
* Then, will be connected to the output in the following code.
#### CPU
* The only way you need to do is connect the data from the ID stage to the EXE stage.
### each step test
* after we write the scala code, we need to test
```
$ sbt "testOnly riscv.singlecycle.InstructionFetchTest"
$ sbt "testOnly riscv.singlecycle.InstructionDecoderTest"
$ sbt "testOnly riscv.singlecycle.ExecuteTest"
```
### Functional Test
* and then we can test some function.
* already provide some code in `ca2023-lab3/csrc`, like 'Fibonacci sequence'
* you can use the `sbt test` to test the code, if you write correct code you should pass all test. Like...
> .......
> [info] <span style="color:green">FibonacciTest:</span>
> [info] <span style="color:green">Single Cycle CPU</span>
> [info] <span style="color:green">- should recursively calculate Fibonacci(10)</span>
> .......
### Modify the homework2's assembly code that can run in MyCPU
#### rewite some instruction in my assembly code
* change the [assembly code](https://github.com/y0y0alex/ca2023-lab3/blob/main/csrc/logclz.S) named `logclz.S` that `j ra` to `jal back1`
* Change the assembly code that instruction `mv` into `addi`
* Modify the Makefile, add the `logclz.asmbin`
```clike=24
BINS = \
logclz.asmbin \
fibonacci.asmbin \
hello.asmbin \
mmio.asmbin \
quicksort.asmbin \
sb.asmbin \
```
* After those modify you can use the `make update` at dir `ca2023-lab3/csrc` , it will generate the `.asmbin` file and update to dir `ca2023-lab3/src/main/resources`
* that you already finish this step.
### Modify the CPUTest.scala
* Code information
* calculate the answer of $log_{2} 64$, the answer should be 6.
* First I write the code like...
```scala=118
class logclz extends AnyFlatSpec with ChiselScalatestTester {
behavior.of("Single Cycle CPU")
it should "show the answer should be 6" in {
test(new TestTopModule("logclz.asmbin")).withAnnotations(TestAnnotations.annos) { c =>
for (i <- 1 to 50) {
c.clock.step()
c.io.mem_debug_read_address.poke((i * 4).U) // Avoid timeout
}
c.io.regs_debug_read_address.poke(10.U)
c.io.regs_debug_read_data.expect(6.U)
}
}
}
```
* As following, we only focus on `logclz` class execute, because other classes are already seccesss.
* When I first run the test `sbt test`
> [info] <span style="color:green">logclz:</span>
> [info] <span style="color:green">Single Cycle CPU</span>
> [info] <span style="color:red">- should show the answer should be 6 *** FAILED ***</span>
> [info] <span style="color:red">io_regs_debug_read_data=0 (0x0) did not equal expected=6 (0x6) (lines in CPUTest.scala: 127, 121)(CPUTest.scala:127)</span>
* At first, I thought it was an assembly code syntax error. But that not true.
* After I study about `c.clock.step()` function, I discuss that is the reason I fail. I don't give it enough time to run.
* After I change the time I give...
```scala=118
class logclz extends AnyFlatSpec with ChiselScalatestTester {
behavior.of("Single Cycle CPU")
it should "show the answer should be 6" in {
test(new TestTopModule("logclz.asmbin")).withAnnotations(TestAnnotations.annos) { c =>
for (i <- 1 to 50000) {
c.clock.step()
c.io.mem_debug_read_address.poke((i * 4).U) // Avoid timeout
}
c.io.regs_debug_read_address.poke(10.U)
c.io.regs_debug_read_data.expect(6.U)
}
}
}
```
* run the test
> [info] <span style="color:green">logclz:</span>
> [info] <span style="color:green">Single Cycle CPU</span>
> [info] <span style="color:green">- should show the answer should be 6</span>
* Some point we should focus on...
1. class name `logclz`.
2. TestTopModule() you need to insert thet you make call `logclz.asmbin`.
3. `for (i <- 1 to 50000)` I use the number `50000`, you can adjust the number that fit your code.
4. `c.io.regs_debug_read_address.poke(10.U)` the number `10` is address of register. My code store the final answer in register `a0` (you can see the address in Ripes that used in homework1).
5. `c.io.regs_debug_read_data.expect(6.U)` 6 is the value you expect.
* Show the whole `sbt test`
> [info] welcome to sbt 1.9.7 (Ubuntu Java 11.0.20.1)
> [info] loading settings for project ca2023-lab3-build from plugins.sbt ...
> [info] loading project definition from /home/ca/ca2023-lab3/project
> [info] loading settings for project root from build.sbt...
> [info] set current project to mycpu (in build file:/home/ca/ca2023-lab3/)
> [info] compiling 1 Scala source to /home/ca/ca2023-lab3/target/scala-2.13/test-classes ...
> [info] <span style="color:green">ExecuteTest:</span>
> [info] <span style="color:green">Execution of Single Cycle CPU</span>
> [info] <span style="color:green">- should execute correctly</span>
> [info] <span style="color:green">QuicksortTest:</span>
> [info] <span style="color:green">Single Cycle CPU</span>
> [info] <span style="color:green">- should perform a quicksort on 10 numbers</span>
> [info] <span style="color:green">InstructionFetchTest:</span>
> [info] <span style="color:green">InstructionFetch of Single Cycle CPU</span>
> [info] <span style="color:green">- should fetch instruction</span>
> [info] <span style="color:green">InstructionDecoderTest:</span>
> [info] <span style="color:green">InstructionDecoder of Single Cycle CPU</span>
> [info] <span style="color:green">- should produce correct control signal</span>
> [info] <span style="color:green">RegisterFileTest:</span>
> [info] <span style="color:green">Register File of Single Cycle CPU</span>
> [info] <span style="color:green">- should read the written content</span>
> [info] <span style="color:green">- should x0 always be zero</span>
> [info] <span style="color:green">- should read the writing content</span>
> [info] <span style="color:green">ByteAccessTest:</span>
> [info] <span style="color:green">Single Cycle CPU</span>
> [info] <span style="color:green">- should store and load a single byte</span>
> [info] <span style="color:green">logclz:</span>
> [info] <span style="color:green">Single Cycle CPU</span>
> [info] <span style="color:green">- should show the answer should be 6</span>
> [info] <span style="color:green">FibonacciTest:</span>
> [info] <span style="color:green">Single Cycle CPU</span>
> [info] <span style="color:green">- should recursively calculate Fibonacci(10)</span>
> [info] <span style="color:blue">Run completed in 1 minute, 3 seconds.</span>
> [info] <span style="color:blue">Total number of tests run: 10</span>
> [info] <span style="color:blue">Suites: completed 8, aborted 0</span>
> [info] <span style="color:blue">Tests: succeeded 10, failed 0, canceled 0, ignored 0, pending 0</span>
> [info] <span style="color:green">All tests passed.</span>
> [<span style="color:green">success</span>] Total time: 75 s (01:15), completed 2023年11月22日 下午6:23:17
## Check Waveform
* use
> $ WRITE_VCD=1 sbt test
> $ gtkwave
### InstructionFetch

* When `io.jump_flag = 1`, `io.instruction_address` will be set that instruction want to jump `0000100c` -> `00001000` .
### InstructionDecode

* We can see the `opcode` = `23`, so that we check the code. This instruction is `aupic`, it is L type.
### Execute

* When `aluop1_source` become True, so we can see `io.instruction_address` is op1.
## Verilator
### make verilator
> sbt "runMain board.verilator.VerilogGenerator"
> [info] welcome to sbt 1.9.7 (Ubuntu Java 11.0.20.1)
> [info] loading settings for project ca2023-lab3-build from plugins.sbt ...
> [info] loading project definition from /home/ca/ca2023-lab3/project
> [info] loading settings for project root from build.sbt ...
> [info] set current project to mycpu (in build file:/home/ca/ca2023-lab3/)
> [info] running board.verilator.VerilogGenerator
> [<span style="color:green">success</span>] Total time: 10 s, completed 2023年11月23日 上午1:05:15
### ./run-verilator.sh -instruction src/main/resources/logclz.asmbin
> -time 10000
> -memory 1048576
> -instruction src/main/resources/logclz.asmbin
> [-------------------->] 100%
### hexdump src/main/resources/mul_clz.asmbin | head -1
> 0000000 0297 0000 8293 1842 a503 0002 a583 0042

* You can see `0297 0000` and the value of `mem_io_instruction` are same.
* So the above waveform can show the instruction is correct.