---
# System prepended metadata

title: 'Lab3: Construct RISC-V CPU with Chisel'

---

# Lab3: Construct RISC-V CPU with Chisel

## Cheat Sheet

https://github.com/freechipsproject/chisel-cheatsheet/releases/latest/download/chisel_cheatsheet.pdf

https://gist.github.com/heathermiller/2ab9ef36910fdfdd20e9

https://www.aboutrv.com/tools/disassembler?code=MHgwZDg3ZjA1NwpjMiAyMCAyNyA3Mw%3D%3D

https://github.com/freechipsproject/diagrammer?tab=readme-ov-file

```
println(getVerilog(new MyOperatorsTwo))
```

Debug in Chisel

```scala
class PrintingModule extends Module {
    val io = IO(new Bundle {
        val in = Input(UInt(4.W))
        val out = Output(UInt(4.W))
    })
    io.out := io.in

    printf("Print during simulation: Input is %d\n", io.in)
    // chisel printf has its own string interpolator too
    printf(p"Print during simulation: IO is $io\n")

    println(s"Print during generation: Input is ${io.in}")
}

test(new PrintingModule ) { c =>
    c.io.in.poke(3.U)
    c.clock.step(5) // circuit will print
    
    println(s"Print during testing: Input is ${c.io.in.peek()}")
}
```

Set the environment variable WRITE_VCD to 1, waveform files will be generated.
```shell
$ WRITE_VCD=1 sbt test
```

https://luplab.gitlab.io/rvcodecjs/

myCpu need to load the instruction into mem.
myCpu will start instuction decode until the `instruction_valid` signal goes `high`
I can start to trace the instuction after `instruction_valid` signal goes `high`

>TestTopModule orchestration:
Before load_finished: ROMLoader writes to Memory, CPU receives zero data
After load_finished: CPU executes from Memory, ROMLoader is idle

## Environment Preparation

```shell
$ java -version
openjdk version "1.8.0_462"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_462-b08)
OpenJDK 64-Bit Server VM (Temurin)(build 25.462-b08, mixed mode)
$ javac -version
javac 1.8.0_462
```

use Surfer to view waveform

```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install --git https://gitlab.com/surfer-project/surfer.git --tag v0.4.0 surfer
```

## Chisel Bootcamp

```bash
docker run -it --rm -p 8888:8888 ucbbar/chisel-bootcamp
```

{%preview https://hackmd.io/Ds_csJawToSNGAhhpHJtWQ %}

## 0-minimal RISC-V CPU jit.asmbin from Quiz-3 Problem A

disassembling from a bin
```shell
$ riscv-none-elf-objdump -D -b binary -m riscv jit.asmbin

   0:	00000297          	auipc	t0,0x0
   4:	03428293          	addi	t0,t0,52 # 0x34
   8:	00000317          	auipc	t1,0x0
   c:	02430313          	addi	t1,t1,36 # 0x2c
  10:	0002a383          	lw	t2,0(t0)
  14:	00732023          	sw	t2,0(t1)
  18:	0042a383          	lw	t2,4(t0)
  1c:	00732223          	sw	t2,4(t1)
  20:	00000297          	auipc	t0,0x0
  24:	00c28293          	addi	t0,t0,12 # 0x2c
  28:	000280e7          	jalr	t0
  2c:	00000013          	nop
  30:	00000013          	nop
  34:	02a00513          	li	a0,42
  38:	00008067          	ret
```

source code
```c
.data
jit_instructions:
    .word 0x02A00513    # addi a0, zero, 42
    .word 0x00008067    # jalr zero, ra, 0 (ret)

.text
.globl _start
_start:
    la t0, jit_instructions
    la t1, jit_code_buffer
    lw t2, 0(t0)
    sw t2, 0(t1)
    lw t2, 4(t0)
    sw t2, 4(t1)

    la t0, jit_code_buffer
    jalr ra, t0, 0
jit_code_buffer:
    .word 0x00000013    # nop (placeholder, will be overwritten)
    .word 0x00000013    # nop (placeholder, will be overwritten)
```

## Fully understand and verify 0-minimal carefully

Comments marked with `CA25: Exercise` in the source indicate TODOs that must be completed as part of the exercises.

## 1-signle-cycle

```
sbt "project singleCycle" "testOnly *InstructionDecoderTest"
```

### CA25: Exercise 4

```scala
object ALUOp1Source {
  val Register           = 0.U(1.W)
  val InstructionAddress = 1.U(1.W)
}

object ALUOp2Source {
  val Register  = 0.U(1.W)
  val Immediate = 1.U(1.W)
}
```

`beq     ra(x1), sp(x2), pc + 2`

### CA25: Exercise 7

RV32I provides a 32-bit address space that is byte-addressed[^riscv-unprivileged.pdf]

`log2Up` is 
https://www.chisel-lang.org/api/latest/chisel3/util/log2Up$.html

```scala
val EntryAddress = 0x1000.U(Parameters.AddrWidth)
```

```shell
$ riscv-none-elf-objdump -D -b binary -m riscv 1-single-cycle/src/main/resources/sb.asmbin

Disassembly of section .data:

0000000000000000 <.data>:
   0:   00400513                li      a0,4
   4:   deadc2b7                lui     t0,0xdeadc
   8:   eef28293                addi    t0,t0,-273 # 0xffffffffdeadbeef
   c:   00550023                sb      t0,0(a0)
  10:   00052303                lw      t1,0(a0)
  14:   01500913                li      s2,21
  18:   012500a3                sb      s2,1(a0)
  1c:   00052083                lw      ra,0(a0)
  20:   0000006f                j       0x20
```

Becasue the Rom EntryAddress is `0x1000` .
Need to Modify the Ripes Compiler Setting `.text section start address` to `0x00001000`

Result in Ripes

Register 

x1(ra)  is 0x000015ef
x5(t0)  is 0xdeadbeef
x6(t1)  is 0x000000ef
x10(a0) is 0x00000004
x18(s2) is 0x00000015

Memory

| Address    | Byte 0 | Byte 1 | Byte 2 | Byte3 |
| ---------- | ------ |:------:|:------:|:-----:|
| 0x00000004 | 0xef   |  0x15  |  0x00  | 0x00  |

```shell
WRITE_VCD=1 sbt "project singleCycle" "testOnly *ByteAccessTest"
```

```scala
writeData := data(7, 0) << (mem_address_index << 3) //Left shift by (mem_address_index * 8) bits
```

From `common/src/main/scala/riscv/core/RegisterFile.scala` says 
> // Map x1-x31 to indices 0-30 in physical storage

I can use the surfer to grab the waveform in `cpu-regs`
`register_0[31:0]` is x1

```scala
c.io.regs_debug_read_address.poke(6.U) // is reg X6
```
The Chisel Resiger Final Result is 

|  wave form name   | Rgister | alias |   Value    |
|:-----------------:|:-------:|:-----:|:----------:|
| register_0[31:0]  |   x1    |  ra   | 0x000015ef |
| register_4[31:0]  |   x5    |  t0   | 0xdeadbeef |
| register_5[31:0]  |   x6    |  t1   | 0x000000ef |
| register_9[31:0]  |   x10   |  a0   | 0x00000004 |
| register_17[31:0] |   x18   |  s2   | 0x00000015 |

**The Chisel MyCpu register is identical to the Ripes register.**



### Integration Tests Fibonacci(10) program

```shell
$ sbt "project singleCycle" "testOnly *FibonacciTest"
```
Use Ripes to Load the `Flat Binary`
Load at `0x00001000`
Entry Point at `0x00001000`

Memory Result is 55 0x37 at `0x00000004`

Use Ripes to Figure witch instruction to save the result in memory 

`    10f4:        00f4a023        sw x15 0 x9`
recap: sw rs2, imm(rs1)

imm is 0
x15(a5) is `fib(10) result`
x9(s1) is memory address `0x00000004`

```shell
$ WRITE_VCD=1 sbt "project singleCycle" "testOnly *FibonacciTest"

$ surfer 1-single-cycle/test_run_dir/Single_Cycle_CPU__Integration_Tests_should_correctly_execute_recursive_Fibonacci10_program/TestTopModule.vcd
```

```shell
$ riscv-none-elf-objdump -D -b binary -m riscv 1-single-cycle/src/main/resources/fibonacci.asmbin

# Display Register in xN, no-aliases is no pseudo
$ riscv64-unknown-elf-objdump -M numeric,no-aliases -D -b binary -m riscv 1-single-cycle/src/main/resources/fibonacci.asmbin 
```

I Found These Instructions used from dissabmley code.
I will expand the `singlecycle/ExecuteTest.scala` to test these Instructions.

* pseudo instruction
    - li (addi or lui & addi)
    - mv(addi)
    - ret(jalr x0, ra, 0)
    - j offset (jal x0, offset)

* R Type
    - [x] add
        - [x] Decode
        - [x] Execute
* I Type
    - [x] addi
        - [x] Decode
        - [x] Execute
    - [x] lw
        - [x] Decode
        - [x] MemoryAccess
* S Type
    - [x] sw
        - [x] Decode
        - [x] MemoryAccess
* B Type
    - [x] beq
        - [x] Decode
        - [x] Execute
    - [x] bgeu
        - [x] Decode
        - [x] Execute
    - [x] bne
        - [x] Decode
        - [x] Execute
* J Type
    - [x] jal
        - [x] Deocde
        - [x] Execute
        - [ ] WriteBack (Need to Check return address is pc + 4)
* U Type
    - [x] lui
        - [x] Decode
        - [x] WriteBack
    - [x] auipc
        - [x] Decode
        - [x] WriteBack
### RISCOF Compliance Testing

All Pass in Signle Cycle CPU

## 2-mmio-trap

```bash
WRITE_VCD=1 sbt "project mmioTrap" "testOnly *C
LINTCSRTest"
```

```bash
$ sbt "project mmioTrap" test

[info] Tests: succeeded 9, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
```

```bash
$ make compliance
```

Result : 119Passed, 0Failed

## 3-pipeline

```bash
$ sbt "project pipeline" "testOnly *PipelineRegisterTest"
```
### Pipeline in mind

IF → ID → EX → MEM → WB

### Phase 2: Data Forwarding (Exercises 17, 18) Test Fail

edit `PipelineConfigs.scala` to remain the `FiveStageFinal`

```bash
$ WRITE_VCD=1 sbt "project pipeline" "testOnly 
*PipelineProgramTest"
```

- [x] Forwarding.scala
- [x] IF2ID.scala
- [x] Control.scala

```scala
    // a) Jump dependency: ADD x1, x2, x3 [EX]; JALR x0, x1, 0 [ID] → stall
    // b) Load-use: LW x1, 0(x2) [EX]; ADD x3, x1, x4 [ID] → stall
    // c) Load-branch: LW x1, 0(x2) [EX]; BEQ x1, x4, label [ID] → stall
```

## Learn

I realize the fibonacci.asmbin will detect the ALU, branches, memory related problems.

### riscv-none-elf-objdump
https://man7.org/linux/man-pages/man1/objdump.1.html

riscv-none-elf-objdump decode branch or jump address to add the pc address 
pc @ 00f71663 deocde result is `bne x14, x15, 12`

```
  80:   00f71663                bne     a4,a5,0x8c
```
https://github.com/bminor/binutils-gdb/blob/7596c998e157149df7e5b0b31580e3707d0b5000/opcodes/riscv-dis.c#L387

`--disassembler-color=extended`
Enables or disables the use of colored syntax highlighting in disassembly output

### riscv-privileged.pdf

The standard RISC-V ISA sets aside a 12-bit encoding space (csr[11:0]) for up to 4,096 CSRs. 
By convention, the upper 4 bits of the CSR address (csr[11:8]) are used to encode the read and write accessibility of the CSRs according to privilege level as shown in Table 3. 
The top two bits (csr[11:10]) indicate whether the register is read/write (00,01, or 10) or **read-only (11)**. 
The next two bits (csr[9:8]) encode the lowest privilege level that can access the CSR.

### Create a Pull Request

Fix PipelineRegister.scala comment

### Run Large Program

{%preview https://hackmd.io/OLeERomFSxatA_mUX--N6g %}

## Reference

[^riscv-unprivileged.pdf]: riscv-unprivileged.pdf