# Assignment3: single-cycle RISC-V CPU Contributed by [P76121089](https://github.com/00853029/ca2023-lab3) ## Environment and Setup > Follow [Lab3: Construct a single-cycle RISC-V CPU with Chisel](https://hackmd.io/@sysprog/r1mlr3I7p) Virtual Box: **ubuntu22.04.3** Install **sdkman** Install **Eclipse Temurin JDK 11** ## Single-cycle RISC-V CPU #### Get the repository: ```shell $ git clone https://github.com/sysprog21/ca2023-lab3 ``` ![image](https://hackmd.io/_uploads/BkaeaDPH6.png) ### Finish MyCPU #### 1. InstructionFetch The original code forgot to control the PC (Program Counter). Therefore, I added a condition to determine whether to jump, deciding whether the PC value should go to the `jump_address` or `pc + 4`(move to next instruction). > Source: `./ca2023-lab3/src/main/scala/riscv/core/InstructionFetch.scala` #### 2. InstructionDecode The original code didn't determine the control signals for `memory_read_enable`(*MemRead*) and `memory_write_enable`(*MemWrite*). Therefore, I added conditions to control these two memory control signals.(by opcode value) ![image](https://hackmd.io/_uploads/BJ2MpwPr6.png) > Source: `src/main/scala/riscv/core/InstructionDecode.scala` #### 3. Execute The original code didn't pass signal lines into the ALU for computation. Therefore, I included the `op1`, `op2` resources, and `func` control signal into the ALU module. Then, the `alu.io.result` will be connected to the output in the following code. > Source: `src/main/scala/riscv/core/Execute.scala` #### 4. CPU I added connections between the id module and exe module that were previously unconnected in the original code. > Source: `src/main/scala/riscv/core/CPU.scala` ### Run Test program using MyCPU #### Preparatory Work - Modify the previous assembly code to allow storing the answer to a specified memory address, enabling verification of the answer at the same address in the testbench code. ```c # Clear the memory intended to store the answer li t0, 0 sw zero, 0(t0) sw zero, 4(t0) sw zero, 8(t0) sw zero, 12(t0) sw zero, 16(t0) ... Print: slli s0,t6,2 add s1,sp,s0 lw a0,0(s1) #*store answer to the specified memory address* slli t5, t6, 2 sw a0, 0(t5) #------------------------ li a7,2 ecall addi t6,t6,1 la a0,_EOL li a7,4 ecall beq t6,s11,End jal Print ``` #### 1. Compile pervious assembly code to `asmbin` file - Put the previous assembly code into the directory(`ca2023-lab3/csrc`) - Modify Makefile ```shell BINS = \ shellSort.asmbin \ ... ``` - Use `make update` operation to compile all file in the directory. #### 2. Modify CPUTest - Setting the memory address where the final answer will be stored - Specifying the expected outcome. > `src/test/scala/riscv/singlecycle/CPUTest.scala` ```scala class shellSort extends AnyFlatSpec with ChiselScalatestTester { behavior.of("Single Cycle CPU") it should "shellSort function" in { test(new TestTopModule("shellSort.asmbin")).withAnnotations(TestAnnotations.annos) { c => for (i <- 1 to 10000) { c.clock.step(1000) c.io.mem_debug_read_address.poke((i * 4).U) // Avoid timeout } //check answer begin //***memory*** c.io.mem_debug_read_address.poke(0.U) c.clock.step() c.io.mem_debug_read_data.expect(0xbfc00000L.U) c.io.mem_debug_read_address.poke(4.U) c.clock.step() c.io.mem_debug_read_data.expect(0xbfa60000L.U) c.io.mem_debug_read_address.poke(8.U) c.clock.step() c.io.mem_debug_read_data.expect(0xbf8c0000L.U) c.io.mem_debug_read_address.poke(12.U) c.clock.step() c.io.mem_debug_read_data.expect(0x3f990000L.U) c.io.mem_debug_read_address.poke(16.U) c.clock.step() c.io.mem_debug_read_data.expect(0x3fb30000L.U) //end--- } } } ``` #### 3. Run Test program using MyCPU **First:** move current directory to ca2023-lab3 ```shell cd ca2023-lab3 ``` **Second:** use `make test > test.log` operation to run all test programs and store the information into test.log. > test.log ```shell $ sbt test [info] welcome to sbt 1.9.7 (Ubuntu Java 11.0.21) [info] loading settings for project ca2023-lab3-build from plugins.sbt ... [info] loading project definition from /home/vboxuser/ca2023-lab3/project [info] loading settings for project root from build.sbt ... [info] set current project to mycpu (in build file:/home/vboxuser/ca2023-lab3/) [info] InstructionDecoderTest: [info] InstructionDecoder of Single Cycle CPU [info] - should produce correct control signal [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] InstructionFetchTest: [info] InstructionFetch of Single Cycle CPU [info] - should fetch instruction [info] ExecuteTest: [info] Execution of Single Cycle CPU [info] - should execute correctly [info] shellSort: [info] Single Cycle CPU [info] - should shellSort function [info] Run completed in 2 minutes, 59 seconds. [info] Total number of tests run: 7 [info] Suites: completed 5, aborted 0 [info] Tests: succeeded 7, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 181 s (03:01), completed Dec 1, 2023, 8:37:39 PM ``` ## Verify the answer waveform using GTKWave. run `WRITE_VCD=1 sbt test` to create waveform. #### 1. Store the answer in the specified memory address. #### 2. write_enable signal = 1 #### 3. write_enable signal = 4'b1111 -> store word - address : 0x00000000 - write_data(answer): 0xBFC00000 ![waveform_ans1](https://hackmd.io/_uploads/SJ4eqdvHa.jpg) - address : 0x00000004 - write_data(answer): 0xBFA60000 ![waveform_ans2](https://hackmd.io/_uploads/rydO9_vBa.jpg) - address : 0x00000008 - write_data(answer): 0xBF8C0000 ![waveform_ans3](https://hackmd.io/_uploads/rkidcOwBp.jpg) - address : 0x0000000C - write_data(answer): 0x3F990000 ![waveform_ans4](https://hackmd.io/_uploads/S1Td9_wr6.jpg) - address : 0x00000010 - write_data(answer): 0x3FB30000 ![waveform_ans5](https://hackmd.io/_uploads/BJ1F9uDHT.jpg)