# 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.