# Single-Cycle RISC-V CPU
> Contribute by [Yu Jui Huang](https://github.com/DarrenHuang0411/NCKU-CA-Assignmrnt3.git)
## 1. Environment
The virtual based Environment is Ubuntu Linux 22.04.
### **1.1 Install verilator and gtkwave**
```
$ sudo apt install build-essential verilator gtkwave
```
### **1.2 Install sbt from SDKMAN**
* **Install SDKMAN**
The Software Development Kit Manager we get from official command is from below URL:
[The official URL of SDKMAN](https://sdkman.io/)
After installation, you need to reference the sdkman source in the Linux system PATH.
```
$ curl -s "https://get.sdkman.io" | bash
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
```
### **1.3 DEB Problem**
reference: [sbt](https://www.scala-sbt.org/release/docs/Installing-sbt-on-Linux.html)
DEB package is officially supported by sbt.
Ubuntu and other Debian-based distributions use the DEB format, but usually you don’t install your software from a local DEB file. Instead they come with package managers both for the command line (e.g. apt-get, aptitude) or with a graphical user interface (e.g. Synaptic). Run the following from the terminal to install sbt (You’ll need superuser privileges to do so, hence the sudo).
```
sudo apt-get update
sudo apt-get install apt-transport-https curl gnupg -yqq
echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list
echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list
curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo -H gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/scalasbt-release.gpg --import
sudo chmod 644 /etc/apt/trusted.gpg.d/scalasbt-release.gpg
sudo apt-get update
sudo apt-get install sbt
```
### **1.4 Install Eclipse Temurin JDK 11 & sbt**
When we install Eclipse Temurin JDK 11
```
$ sdk install java 11.0.21-tem
$ sdk install sbt
```
check software update

### **1.4 Install Eclipse Temurin JDK 11**
* **Install Chisel Repo**
```
$ git clone https://github.com/ucb-bar/chisel-tutorial
$ cd chisel-tutorial
$ git checkout release
```
Try the demo code to understand basic concept.
```
$ sbt run
```
```
[info] Loading project definition from /home/darren/chisel-tutorial/poject
[info] Loading settings for project chisel-tutorial from build.sbt ...
[info] Set current project to chisel-tutorial (in build file:/home/darren/chisel-tutorial/)
[info] Compiling 63 Scala sources to /home/darren/chisel-tutorial/target/scala-2.12/test-classes ...
[warn] Multiple main classes detected. Run 'show discoveredMainClasses' to see the list
[info] running examples.Launcher FullAdder
Starting tutorial FullAdder
[info] [0.001] Elaborating design...
[info] [1.046] Done elaborating.
Computed transform order in: 340.4 ms
Total FIRRTL Compile Time: 710.9 ms
file loaded in 0.061352756 seconds, 13 symbols, 8 statements
[info] [0.001] SEED 1701000013912
test FullAdder Success: 8 tests passed in 9 cycles in 0.025964 seconds 346.63 Hz
[info] [0.006] RAN 4 CYCLES PASSED
Tutorials passing: 1
```
## **2. Finished MyCPU**
The Single-cycle CPU architecture diagram is refered from [Lab3: Construct a single-cycle CPU with Chisel](https://hackmd.io/@sysprog/r1mlr3I7p#Install-sbt)

### **2.1 Instruction Fetch**
The code filled in **`Instruction Fetch`** like below
> [Github Code](https://github.com/DarrenHuang0411/NCKU-CA-Assignmrnt3/tree/main/src/main/scala/riscv/core)
:::danger
:warning: **Refrain from copying and pasting your solution directly into the HackMD note**. Instead, provide a concise summary of the various test cases, outlining the aspects of the CPU they evaluate, the techniques employed for loading test program instructions, and the outcomes of these test cases.
:::
* **Explanation the missing code:**
The update of the Program Counter (PC) value to the address of the next instruction is not implemented correctly.
It is crucial to ensure that in the next clock cycle, the accurate address is utilized for fetching the subsequent instruction.
The handling of PC+4 instruction or jump instruction signals is currently lacking.
### **2.2 Instruction Decode**
The code filled in **`Instruction Decode`** like below
> [Github Code](https://github.com/DarrenHuang0411/NCKU-CA-Assignmrnt3/tree/main/src/main/scala/riscv/core)
:::danger
:warning: **Refrain from copying and pasting your solution directly into the HackMD note**. Instead, provide a concise summary of the various test cases, outlining the aspects of the CPU they evaluate, the techniques employed for loading test program instructions, and the outcomes of these test cases.
:::
* **Explanation the missing code:**
The original code lacked the determination of control signals for memory_read_enable (MemRead) and memory_write_enable (MemWrite). As a remedy, I introduced conditions to govern these two memory control signals based on the opcode value.
### **2.3 Execute**
The code filled in **`Execute`** like below
> [Github Code](https://github.com/DarrenHuang0411/NCKU-CA-Assignmrnt3/tree/main/src/main/scala/riscv/core)
:::danger
:warning: **Refrain from copying and pasting your solution directly into the HackMD note**. Instead, provide a concise summary of the various test cases, outlining the aspects of the CPU they evaluate, the techniques employed for loading test program instructions, and the outcomes of these test cases.
:::
* **Explanation the missing code:**
### **2.4 CPU**
The code filled in Execute like below
> [Github Code](https://github.com/DarrenHuang0411/NCKU-CA-Assignmrnt3/tree/main/src/main/scala/riscv/core)
* **Explanation the missing code:**
## **3. To Test Each Module & Debug Own Mistake**
### **3.1 Instruction Fetch**
```
sbt "testOnly riscv.singlecycle.InstructionFetchTest"
```
The first time I run this part,there are some problems in my code.
> Problem > pc := ==????== jump_address_id
```
[error] /home/darren/ca2023-lab3/src/main/scala/riscv/core/InstructionFetch.scala:29:12: not found: value jump_address_id
[error] pc := jump_address_id
[error] ^
[error] four errors found
```
> Solution > pc := ==io.== jump_address_id
### **3.2 Instruction Decode**
```
sbt "testOnly riscv.singlecycle.InstructionDecoderTest"
```
The same error appeared 2 times
> Problem > opcode == InstructionTypes.L
```
[error] found : Boolean
[error] required: chisel3.Bool
[error] opcode == InstructionTypes.L,
[error] ^
```
> Solution > opcode === InstructionTypes.L
### **3.3 Execute**
```
sbt "testOnly riscv.singlecycle.ExecuteTest"
```
The same error appeared 3 times
```
[error] /home/darren/ca2023-lab3/src/main/scala/riscv/core/Execute.scala:40:14: reassignment to val
[error] alu.io.op1 = Mux(io.aluop1_source.asBool, io.instruction_address , io.reg1_data)
[error] ^
```
```
[info] ExecuteTest:
[info] Execution of Single Cycle CPU
[info] - should execute correctly
[info] Run completed in 6 seconds, 968 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 8 s, completed Nov 30, 2023, 1:04:22 AM
```
### **3.3 Final Test(including CPU)**
```
sbt test
```
The result shows all pass for each module.
```
[info] InstructionDecoderTest:
[info] InstructionDecoder of Single Cycle CPU
[info] - should produce correct control signal
[info] ExecuteTest:
[info] Execution of Single Cycle CPU
[info] - should execute correctly
[info] ByteAccessTest:
[info] Single Cycle CPU
[info] - should store and load a single byte
[info] QuicksortTest:
[info] Single Cycle CPU
[info] - should perform a quicksort on 10 numbers
[info] InstructionFetchTest:
[info] InstructionFetch of Single Cycle CPU
[info] - should fetch instruction
[info] FibonacciTest:
[info] Single Cycle CPU
[info] - should recursively calculate Fibonacci(10)
[info] RegisterFileTest:
[info] Register File of Single Cycle CPU
[info] - should read the written content
[info] - should x0 always be zero
[info] - should read the writing content
[info] Run completed in 34 seconds, 85 milliseconds.
[info] Total number of tests run: 9
[info] Suites: completed 7, aborted 0
[info] Tests: succeeded 9, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
```
## **4. Modify the handwritten assembly code in Homework2**
### **4.1 Remove Print code & Rewrite the result saved back to S0(X8)~S5(X21)**
The original code does not save the results back to S0 (X8) to S5 (X21).
The original code only prints them out.
```
Print:
slli, s0,t6,2
add, s1,sp,s0
lw, a0,0(s1)
li, a7,2
ecall
addi, t6,t6,1
la, a0,_EOL
li, a7,4
ecall
beq, t6,s11,End
jal, Print
```
The code I revise again is like below:
We need to load sp data back to s0~s5.
```
Print:
lw, s0,0(sp)
lw, s1,4(sp)
lw, s2,8(sp)
lw, s3,12(sp)
lw, s4,16(sp)
lw, s5,20(sp)
```
Add file into **`ca2023-lab3/csrc/Makefile`**
> [Github Code](https://github.com/DarrenHuang0411/NCKU-CA-Assignmrnt3/blob/main/csrc/Makefile)
```
BINS = \
shellsort.asmbin\
fibonacci.asmbin \
hello.asmbin \
mmio.asmbin \
quicksort.asmbin \
sb.asmbin
```
### 4.2 Shellsort CPUTest
The Shellsort input data:
```
In s0: dataset1: .word 0x3fcccccd #1.6
In s1: dataset2: .word 0xbfc00000 #-1.5
In s2: dataset3: .word 0x3fb33333 #1.4
In s3: dataset4: .word 0xbfa66666 #-1.3
In s4: dataset5: .word 0x3f99999a #1.2
In s5: dataset6: .word 0xbf8ccccd #-1.1
```
The result we want to get will show:
```
In s0: 0xbfc00000 #-1.5
In s1: 0xbfa66666 #-1.3
In s2: 0xbf8ccccd #-1.1
In s3: 0x3f99999a #1.2
In s4: 0x3fb33333 #1.4
In s5: 0x3fcccccd #1.6
```
In this part, we need to add to part to check the expect answer.
* poke(8.U) ==> 8 is register s0(x8) in riscv32emu
* expect(0xbfc00000L.U)==>0xbfc00000 is the result (-1.5) shown in s0(x8).
```
c.io.regs_debug_read_address.poke(8.U) // s0
c.io.regs_debug_read_data.expect(0xbfc00000L.U)
```
Because we have five test data set, we need to add five registers we want to poke and what final answer we want to get.
The code is like below:
```
class shellsort extends AnyFlatSpec with ChiselScalatestTester {
behavior.of("Single Cycle CPU")
it should "recursively calculate Fibonacci(10)" in {
test(new TestTopModule("shellsort.asmbin")).withAnnotations(TestAnnotations.annos) { c =>
for (i <- 1 to 2000000) {
c.clock.step()
c.io.mem_debug_read_address.poke((i * 4).U) // Avoid timeout
}
c.io.regs_debug_read_address.poke(8.U) // s0
c.io.regs_debug_read_data.expect(0xbfc00000L.U)
c.io.regs_debug_read_address.poke(9.U) // s1
c.io.regs_debug_read_data.expect(0xbfa60000L.U)
c.io.regs_debug_read_address.poke(18.U) // s2
c.io.regs_debug_read_data.expect(0xbf8c0000L.U)
c.io.regs_debug_read_address.poke(19.U) // s3
c.io.regs_debug_read_data.expect(0x3f990000L.U)
c.io.regs_debug_read_address.poke(20.U) // s4
c.io.regs_debug_read_data.expect(0x3fb30000L.U)
c.io.regs_debug_read_address.poke(21.U) // s5
c.io.regs_debug_read_data.expect(0x3fcc0000L.U)
}
}
}
```
```
[info] shellsort:
[info] Single Cycle CPU
[info] - should recursively calculate Fibonacci(10)
[info] Run completed in 1 minute, 34 seconds.
[info] Total number of tests run: 10
[info] Suites: completed 8, aborted 0
[info] Tests: succeeded 10, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
```
## **5. GTKwave Observation**
```
$ WRITE_VCD=1 sbt test
```
### **5.1 The Result of Shellsort**
We observe the signal from Instration Decoder.
* **io_reg_write_enable :** read the dataset to register
* **io_regs_write_data :** read what dataset in
* **rd :** to which register
[](https://hackmd.io/zCc-wifJRyOTjTD5DHOkhQ?both#42-Shellsort-CPUTest)
* **Input Data Checking**

* **Output Data Checking**
