# Rewrite Lab3 as 5-stage pipeline RISC-V processor
contributed by <[linyu0425](https://ithelp.ithome.com.tw/articles/102803095)>
## Extract 5-stage pipeline CPU to HW3
* Move the source code to the HW3 folder.
```shell=
$ git clone https://github.com/howardlau1999/yatcpu/tree/main
```
* Because I only need the five-stage CPU, I will delete unnecessary files from the "/src/main/scala/riscv" directory.
* Remaining files : core debug Parameters.scala
```shell=
make test
```
```
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/board/verilator/Top.scala:11:8: object CPUBundle is not a member of package riscv
[error] import riscv.CPUBundle
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/board/verilator/Top.scala:15:19: not found: type CPUBundle
[error] val io = IO(new CPUBundle)
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/riscv/core/CPUBundle.scala:17:8: not found: object bus
[error] import bus.AXI4LiteChannels
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/board/verilator/Top.scala:25:10: value instruction is not a member of riscv.core.CPUBundle
[error] cpu.io.instruction := io.instruction
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/riscv/core/fivestage/CPU.scala:17:8: not found: object bus
[error] import bus.AXI4LiteMaster
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/riscv/core/CPUBundle.scala:22:27: not found: type AXI4LiteChannels
[error] val axi4_channels = new AXI4LiteChannels(Parameters.AddrBits, Parameters.DataBits)
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/riscv/core/fivestage/CPU.scala:47:32: not found: type AXI4LiteMaster
[error] val axi4_master = Module(new AXI4LiteMaster(Parameters.AddrBits, Parameters.DataBits))
[error] ^
[error] 7 errors found
```
* Because of the error above, I added files to Verilator.
```
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/board/verilator/Top.scala:17:8: not found: object bus
[error] import bus.{AXI4LiteSlave, AXI4LiteSlaveBundle, BusArbiter, BusSwitch}
[error] ^
```
* Added the bus to main directory
```
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/board/verilator/Top.scala:20:8: object DummySlave is not a member of package peripheral
[error] import peripheral.DummySlave
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/board/verilator/Top.scala:45:26: not found: type DummySlave
[error] val dummy = Module(new DummySlave)
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/bus/BusSwitch.scala:18:8: object DummyMaster is not a member of package peripheral
[error] import peripheral.DummyMaster
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/bus/BusSwitch.scala:27:26: not found: type DummyMaster
[error] val dummy = Module(new DummyMaster)
[error] ^
[error] four errors found
[error] (Compile / compileIncremental) Compilation failed
```
* Added "DummySlave.scala and DummyMaster.scala" to peripheral directory.
```
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/CPUTest.scala:16:8: object ProgramCounter is not a member of package riscv.core
[error] import riscv.core.ProgramCounter
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/CPUTest.scala:47:42: value instruction_address is not a member of riscv.core.CPUBundle
[error] mem.io.instruction_address := cpu.io.instruction_address
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/CPUTest.scala:48:12: value instruction is not a member of riscv.core.CPUBundle
[error] cpu.io.instruction := mem.io.instruction
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/CPUTest.scala:52:14: value memory_bundle is not a member of riscv.core.CPUBundle
[error] cpu.io.memory_bundle.read_data := 0.U
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/CPUTest.scala:55:14: value memory_bundle is not a member of riscv.core.CPUBundle
[error] cpu.io.memory_bundle <> mem.io.bundle
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/ExecuteTest.scala:9:8: object ALUOp1Source is not a member of package riscv.core
[error] import riscv.core.ALUOp1Source
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/ExecuteTest.scala:10:8: object ALUOp2Source is not a member of package riscv.core
[error] import riscv.core.ALUOp2Source
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/ExecuteTest.scala:11:8: object Execute is not a member of package riscv.core
[error] import riscv.core.Execute
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/ExecuteTest.scala:12:8: object InstructionTypes is not a member of package riscv.core
[error] import riscv.core.InstructionTypes
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/ExecuteTest.scala:18:14: not found: type Execute
[error] test(new Execute).withAnnotations(TestAnnotations.annos) { c =>
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionDecoderTest.scala:9:8: object ALUOp1Source is not a member of package riscv.core
[error] import riscv.core.ALUOp1Source
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionDecoderTest.scala:10:8: object ALUOp2Source is not a member of package riscv.core
[error] import riscv.core.ALUOp2Source
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionDecoderTest.scala:11:8: object InstructionDecode is not a member of package riscv.core
[error] import riscv.core.InstructionDecode
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionDecoderTest.scala:12:8: object InstructionTypes is not a member of package riscv.core
[error] import riscv.core.InstructionTypes
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionDecoderTest.scala:18:14: not found: type InstructionDecode
[error] test(new InstructionDecode).withAnnotations(TestAnnotations.annos) { c =>
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionFetchTest.scala:12:8: object ALUOp1Source is not a member of package riscv.core
[error] import riscv.core.ALUOp1Source
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionFetchTest.scala:13:8: object ALUOp2Source is not a member of package riscv.core
[error] import riscv.core.ALUOp2Source
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionFetchTest.scala:14:8: object InstructionFetch is not a member of package riscv.core
[error] import riscv.core.InstructionFetch
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionFetchTest.scala:15:8: object InstructionTypes is not a member of package riscv.core
[error] import riscv.core.InstructionTypes
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionFetchTest.scala:16:8: object ProgramCounter is not a member of package riscv.core
[error] import riscv.core.ProgramCounter
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/InstructionFetchTest.scala:23:14: not found: type InstructionFetch
[error] test(new InstructionFetch).withAnnotations(TestAnnotations.annos) { c =>
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/RegisterFileTest.scala:9:8: object RegisterFile is not a member of package riscv.core
[error] import riscv.core.RegisterFile
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/RegisterFileTest.scala:15:14: not found: type RegisterFile
[error] test(new RegisterFile).withAnnotations(TestAnnotations.annos) { c =>
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/RegisterFileTest.scala:28:14: not found: type RegisterFile
[error] test(new RegisterFile).withAnnotations(TestAnnotations.annos) { c =>
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/singlecycle/RegisterFileTest.scala:41:14: not found: type RegisterFile
[error] test(new RegisterFile).withAnnotations(TestAnnotations.annos) { c =>
[error] ^
[error] 25 errors found
```
* Added test program to main/test.
```
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/PeripheralTest.scala:29:39: type Timer is not a member of package peripheral
[error] val timer = Module(new peripheral.Timer)
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/PeripheralTest.scala:77:37: value channels is not a member of chisel3.Bundle{val bundle: peripheral.RAMBundle; val instruction: chisel3.UInt; val instruction_address: chisel3.UInt; val debug_read_address: chisel3.UInt; val debug_read_data: chisel3.UInt}
[error] master.io.channels <> memory.io.channels
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/PeripheralTest.scala:126:19: value load_start is not a member of chisel3.Bundle{val bundle: peripheral.RAMBundle; val rom_address: chisel3.UInt; val rom_data: chisel3.UInt; val load_address: chisel3.UInt; val load_finished: chisel3.Bool}
[error] rom_loader.io.load_start := io.load_start
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/PeripheralTest.scala:133:40: value channels is not a member of chisel3.Bundle{val bundle: peripheral.RAMBundle; val rom_address: chisel3.UInt; val rom_data: chisel3.UInt; val load_address: chisel3.UInt; val load_finished: chisel3.Bool}
[error] slave.io.channels <> rom_loader.io.channels
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/fivestage/CPUTest.scala:17:14: object basys3 is not a member of package board
[error] import board.basys3.BootStates
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/fivestage/CPUTest.scala:63:28: not found: value BootStates
[error] val boot_state = RegInit(BootStates.Init)
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/fivestage/CPUTest.scala:69:37: type Timer is not a member of package peripheral
[error] val timer = Module(new peripheral.Timer)
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/fivestage/CPUTest.scala:79:17: value load_start is not a member of chisel3.Bundle{val bundle: peripheral.RAMBundle; val rom_address: chisel3.UInt; val rom_data: chisel3.UInt; val load_address: chisel3.UInt; val load_finished: chisel3.Bool}
[error] rom_loader.io.load_start := false.B
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/fivestage/CPUTest.scala:83:37: value channels is not a member of chisel3.Bundle{val bundle: peripheral.RAMBundle; val instruction: chisel3.UInt; val instruction_address: chisel3.UInt; val debug_read_address: chisel3.UInt; val debug_read_data: chisel3.UInt}
[error] bus_switch.io.slaves(0) <> mem.io.channels
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/fivestage/CPUTest.scala:84:17: value channels is not a member of chisel3.Bundle{val bundle: peripheral.RAMBundle; val rom_address: chisel3.UInt; val rom_data: chisel3.UInt; val load_address: chisel3.UInt; val load_finished: chisel3.Bool}
[error] rom_loader.io.channels <> dummy.io.channels
[error] ^
[error] 10 errors found
```
* Added Memory.scala and timer.scala to peripheral directory.
```
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/peripheral/Dummy.scala:12:30: not found: type RAMBundle
[error] val bundle = Flipped(new RAMBundle)
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/main/scala/peripheral/ROMLoader.scala:11:30: not found: type RAMBundle
[error] val bundle = Flipped(new RAMBundle)
[error] ^
[error] two errors found
```
* Added ROM_bundal.scala to peripheral.
```
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/fivestage/CPUTest.scala:17:14: object basys3 is not a member of package board
[error] import board.basys3.BootStates
[error] ^
[error] /home/linyu/Desktop/ca2023-final/src/test/scala/riscv/fivestage/CPUTest.scala:63:28: not found: value BootStates
[error] val boot_state = RegInit(BootStates.Init)
[error] ^
[error] two errors found
```
* Added basys3 to scala directory
## After make test
```
[info] TimerTest:
[info] Timer
[info] - should read and write the limit
[info] MMIOTest:
[info] Five Stage CPU
[info] - should read and write timer register
[info] MemoryTest:
[info] Memory
[info] - should perform read and write
[info] ByteAccessTest:
[info] Five Stage CPU
[info] - should store and load single byte
[info] ROMLoaderTest:
[info] ROMLoader
[info] - should load program
[info] RegisterFileTest:
[info] Register File of Five-stage CPU
[info] - should read the written content
[info] - should x0 always be zero
[info] - should read the writing content
[info] QuicksortTest:
[info] Five Stage CPU
[info] - should quicksort 10 numbers
[info] FibonacciTest:
[info] Five Stage CPU
[info] - should calculate recursively fibonacci(10)
[info] Run completed in 22 seconds, 236 milliseconds.
[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.
```
## Verilator Test
```shell=
$ make verilator
```
```
../sim_main.cpp:207:18: error: ‘class VTop’ has no member named ‘io_instruction_valid’
207 | top->io_instruction_valid = 1;
| ^~~~~~~~~~~~~~~~~~~~
```
* Added sim_main.cpp to verilator directory.
```shell=
$ ./run-verilator.sh -instruction src/main/resources/fibonacci.asmbin -time 2000 -vcd dump.vcd
```
* Run `gtkwave dump.vcd` to check its waveform.
### IF

* In the IF2ID module, there are three signals - instruction, instruction_address, and interrupt - that need to be stored in registers for the next ID stage to use.
* When write_enable is set to 1, instruction_in and instruction_address_in will be written to their respective registers. In the waveform, you can observe that whenever instruction_in and instruction_address_in change, instruction_out and instruction_address_out will change on the next clock cycle.

* In the ID stage, the inputs also receive values stored in registers in the IF2ID stage.
### ID

* In the ID stage, signals are generated based on the opcode and written into registers for the next EXE stage to use.
* When the register input signal changes, the register output signal will change on the next clock cycle, providing it to the next stage.
### EXE

* The upper diagram illustrates the transmission of the memWrite signal from the EXE stage to the MEM stage. The two signals below are registers entering the MEM stage from the EXE stage. The MEM stage's register input signal will be the same as the EXE stage's register output signal, and the MEM stage's register output signal will obtain the value on the next clock cycle.
### MEM

* In the MEM stage, since the current MemWrite is 1, it will read from memory. Due to the need for multiple clock cycles to write into memory, the ctrl_sign will be written into the register instead of being written directly. This ensures that the instruction signal is maintained until the write operation is complete.

* So, as seen in the above diagram, after the stall ends, the values need to be written into the WB-stage register for it to update.
### WB

* In the WB stage, the values and addresses that need to be written back to the register file will be sent back to the IF stage.