---
# System prepended metadata

title: 'Assignment3: Your Own RISC-V CPU'

---

# Assignment3: Your Own RISC-V CPU

## [Chisel Bootcmap](https://github.com/sysprog21/chisel-bootcamp) Notes
### Scala v.s. Chisel
* Scala: `1 + 1`
    Chisel: `1.U + 1.U` or `1.S + 1.S`
* Scala: `=`, software assignment
    Chisel: `:=`, hardware connection
* Scala: `==`
    Chisel: `===`
* Scala:
    ```Scala
    if () {

    }else if () {

    }else () {

    }
    ```
    Chisel:
    ```Scala
    when() {

    }.elsewhen() {

    }.otherwise {

    } 
    ```

### Common usage
* `3'h4`=`= 3'b100`: The 3-bit constant `4`(`100`)
* `4.W`: 4 bits wide
* `UInt(4.W)`: An unsigned integer with a 4-bit width
*  Mux: `Mux(select, trueValue, falseValue)`
*  Concatenate: `Cat(10, 1) = 101`
*  Define the IO interface: `val io = IO(new Bundle { ... })
*  For: `for (i <- 0 until 10) { ... } `
*  Saturate: 
    ```scala
    val A = 10.U(4.W) // A = 1010
    val B = 7.U(4.W)  // B = 0111
    ```
    ```
    A +& B = 0001
    A + B = 10001
    ```

## 1-single-cycle
> Commit [80e0e29](https://github.com/sysprog21/ca2025-mycpu/commit/80e0e29b06fe1d23f2e955648ad06d7e2a77f307)
### Exercise 3

The function `ALUFunctions()` is in path `common/src/main/scala/riscv/core/ALU.scala`

### Exercise 4
* For the B-type, ALU is for calculating `PC+immediate`, and branch comparison is done by branch comparator. For example, we should do `io.reg1_data === io.reg2_data` instead of `alu.io.op1 === alu.io.op2` when comparing the register data.

* Use `.asSInt` before signed comparison
    ```scala
    // Signed comparison (need conversion to signed type)
    InstructionsTypeB.blt  -> io.reg1_data.asSInt < io.reg2_data.asSInt,
    InstructionsTypeB.bge  -> io.reg1_data.asSInt >= io.reg2_data.asSInt,
    ```

    And so for unsigned comparison
    ```scala
    // Unsigned comparison
    InstructionsTypeB.bltu -> io.reg1_data.asUInt < io.reg2_data.asUInt,
    InstructionsTypeB.bgeu -> io.reg1_data.asUInt >= io.reg2_data.asUInt
    ```

* If we write:
    ```scala
    InstructionsTypeB.beq -> io.reg1_data === io.reg2_data
    ```
    Scala will parse as:
    ```scala
    (InstructionsTypeB.beq -> io.reg1_data) === io.reg2_data
    ```
    The correct way to write is:
    ```scala
    InstructionsTypeB.beq -> (io.reg1_data === io.reg2_data)
    ```
### Exercise 7
Be cautious: `Cat` not `cat`.

## 2-mmio-trap
> Commit [4de5115](https://github.com/sysprog21/ca2025-mycpu/commit/4de51153143beaab015b9ef153c73dd9ce1e9d37)

## 3-pipeline
> Commit [9d1c95d](https://github.com/sysprog21/ca2025-mycpu/commit/9d1c95d157498cec6e419c5aecd90b05701d5e44)

* Q: Why do we need to stall for load-use hazards? (Hint: Consider data dependency and forwarding limitations)

    A: The instruction at EX stage needs the loaded data, but the loaded data is not available until the end of MEM stage, so we need to stall.
    
* Q: What is the difference between "stall" and "flush" operations? (Hint: Compare their effects on pipeline registers and PC)

    A: Stall is used for data hazards, it freezes the PC and injects NOP into the next stage, and delay until the data becomes available. Flush is used for control hazards, it cleers the pipline registers and inserts NOPs, discard the wrong instructions.
    
* Q: Why does jump instruction with register dependency need stall? (Hint: When is jump target address available?)

    A: Jump instruction calculate the target address at ID stage, if the load instruction is still in MEM stage, the address will not be available.
    
* Q: In this design, why is branch penalty only 1 cycle instead of 2? (Hint: Compare ID-stage vs EX-stage branch resolution)

    A: Because the target address is calculated at the ID stage, rather than the EX stage, so we only need to discard the instruction in the IF/ID register when the branch is taken.
    
* Q: What would happen if we removed the hazard detection logic entirely? (Hint: Consider data hazards and control flow correctness)

    A: Instructions would read incorrect data from the register file, or taken the wrong branches.