diff --git a/3-pipeline/csrc/Makefile b/3-pipeline/csrc/Makefile index 7172372f..77a32b85 100644 --- a/3-pipeline/csrc/Makefile +++ b/3-pipeline/csrc/Makefile @@ -27,8 +27,8 @@ BINS = \ quicksort.asmbin \ sb.asmbin \ uart.asmbin \ - irqtrap.asmbin - + irqtrap.asmbin \ + hazard_extended.asmbin \ # Clear the .DEFAULT_GOAL special variable, so that the following turns # to the first target after .DEFAULT_GOAL is not set. .DEFAULT_GOAL := @@ -39,4 +39,4 @@ update: $(BINS) cp -f $(BINS) ../src/main/resources clean: - $(RM) *.o *.elf *.asmbin + $(RM) *.o *.elf *.asmbin \ No newline at end of file diff --git a/3-pipeline/csrc/hazard_extended.S b/3-pipeline/csrc/hazard_extended.S index 505e2f6d..cf44a108 100644 --- a/3-pipeline/csrc/hazard_extended.S +++ b/3-pipeline/csrc/hazard_extended.S @@ -102,6 +102,20 @@ skip2: skip2_end: sw t1, 0x34(zero) # mem[0x34] = 20 + # ===== Section 11: False Stall ===== + # Test Control hazard that should not cause stalls + # Make a5 a valid base address (reuse sp=0x1000 from Section 2) + addi a5, sp, 0 + + csrr a2, cycle # Read cycle CSR + + lw a6, (a5) # Load from address in a5 (should not stall) + lw a7, 16(a5) # Load from address in a5+16 (should not stall) + + csrr a3, cycle # Read cycle CSR again + sub a3, a3, a2 # a3 = cycle difference (should be small, ≥1) + sw a3, 0x38(zero) # mem[0x38] = cycle difference, should be 3 cycles + # ===== End: Calculate cycle count ===== csrr a1, cycle # End cycle count sub s0, a1, a0 # s0 = total cycles (result register) @@ -120,4 +134,4 @@ skip2_end: # s0 (x8) = cycle count loop: - j loop + j loop \ No newline at end of file diff --git a/3-pipeline/src/main/scala/riscv/core/fivestage_final/CPU.scala b/3-pipeline/src/main/scala/riscv/core/fivestage_final/CPU.scala index e878f410..3de0a641 100644 --- a/3-pipeline/src/main/scala/riscv/core/fivestage_final/CPU.scala +++ b/3-pipeline/src/main/scala/riscv/core/fivestage_final/CPU.scala @@ -137,6 +137,8 @@ class CPU extends Module { ctrl.io.rd_ex := id2ex.io.output_regs_write_address ctrl.io.memory_read_enable_mem := ex2mem.io.output_memory_read_enable ctrl.io.rd_mem := ex2mem.io.output_regs_write_address + ctrl.io.uses_rs2_id := id.io.uses_rs2_id + ctrl.io.uses_rs1_id := id.io.uses_rs1_id regs.io.write_enable := mem2wb.io.output_regs_write_enable regs.io.write_address := mem2wb.io.output_regs_write_address diff --git a/3-pipeline/src/main/scala/riscv/core/fivestage_final/Control.scala b/3-pipeline/src/main/scala/riscv/core/fivestage_final/Control.scala index 91903c67..6f794c40 100644 --- a/3-pipeline/src/main/scala/riscv/core/fivestage_final/Control.scala +++ b/3-pipeline/src/main/scala/riscv/core/fivestage_final/Control.scala @@ -61,6 +61,8 @@ class Control extends Module { val rd_ex = Input(UInt(Parameters.PhysicalRegisterAddrWidth)) // id2ex.io.output_regs_write_address val memory_read_enable_mem = Input(Bool()) // ex2mem.io.output_memory_read_enable // val rd_mem = Input(UInt(Parameters.PhysicalRegisterAddrWidth)) // ex2mem.io.output_regs_write_address // + val uses_rs1_id = Input(Bool()) // true only if current ID instruction really reads rs1 + val uses_rs2_id = Input(Bool()) // true only if current ID instruction really reads rs2 val if_flush = Output(Bool()) val id_flush = Output(Bool()) @@ -84,6 +86,10 @@ class Control extends Module { // 1. Load-use hazard: Load result used immediately by next instruction // 2. Jump-related hazard: Jump instruction needs register value not ready // 3. Control hazard: Branch/jump instruction changes PC + // Hint: For data hazards type 1 and 2, check for register dependencies + // Use the decoded `uses_rs1_id` / `uses_rs2_id` flags to test whether the + // current ID-stage instruction actually reads rs1/rs2; only then should a + // register-number match be considered a true RAW dependency. // // Control signals: // - pc_stall: Freeze PC (don't fetch next instruction) @@ -104,11 +110,11 @@ class Control extends Module { // 3. AND destination register is not x0 // 4. AND destination register conflicts with ID source registers // - ((?) && // Either: + ((io.memory_read_enable_ex || io.jump_instruction_id) && // Either: // - Jump in ID needs register value, OR // - Load in EX (load-use hazard) - ? =/= 0.U && // Destination is not x0 - (?)) // Destination matches ID source + (io.rd_ex =/= 0.U) && // Destination is not x0 + ((io.uses_rs1_id && (io.rd_ex === io.rs1_id) || io.uses_rs2_id && (io.rd_ex === io.rs2_id)))) // Destination matches ID source // // Examples triggering Condition 1: // a) Jump dependency: ADD x1, x2, x3 [EX]; JALR x0, x1, 0 [ID] → stall @@ -125,10 +131,11 @@ class Control extends Module { // 3. Destination register is not x0 // 4. Destination register conflicts with ID source registers // - (? && // Jump instruction in ID - ? && // Load instruction in MEM - ? =/= 0.U && // Load destination not x0 - (?)) // Load dest matches jump source + ( io.jump_instruction_id && // Jump instruction in ID + io.memory_read_enable_mem && // Load instruction in MEM + (io.rd_mem =/= 0.U) && // Load destination not x0 + ((io.uses_rs1_id && (io.rd_mem === io.rs1_id)) || (io.uses_rs2_id && (io.rd_mem === io.rs2_id))) // Load dest matches jump source + ) // // Example triggering Condition 2: // LW x1, 0(x2) [MEM]; NOP [EX]; JALR x0, x1, 0 [ID] @@ -140,9 +147,9 @@ class Control extends Module { // - Flush ID/EX register (insert bubble) // - Freeze PC (don't fetch next instruction) // - Freeze IF/ID (hold current fetch result) - io.id_flush := ? - io.pc_stall := ? - io.if_stall := ? + io.id_flush := true.B + io.pc_stall := true.B + io.if_stall := true.B }.elsewhen(io.jump_flag) { // ============ Control Hazard (Branch Taken) ============ @@ -150,7 +157,7 @@ class Control extends Module { // Only flush IF stage (not ID) since branch resolved early // TODO: Which stage needs to be flushed when branch is taken? // Hint: Branch resolved in ID stage, discard wrong-path instruction - io.if_flush := ? + io.if_flush := true.B // Note: No ID flush needed - branch already resolved in ID! // This is the key optimization: 1-cycle branch penalty vs 2-cycle } diff --git a/3-pipeline/src/main/scala/riscv/core/fivestage_final/InstructionDecode.scala b/3-pipeline/src/main/scala/riscv/core/fivestage_final/InstructionDecode.scala index 3809c0f7..73941373 100644 --- a/3-pipeline/src/main/scala/riscv/core/fivestage_final/InstructionDecode.scala +++ b/3-pipeline/src/main/scala/riscv/core/fivestage_final/InstructionDecode.scala @@ -152,6 +152,8 @@ class InstructionDecode extends Module { val clint_jump_address = Output(UInt(Parameters.AddrWidth)) // clint.io.jump_address val if_jump_flag = Output(Bool()) // ctrl.io.jump_flag , inst_fetch.io.jump_flag_id val if_jump_address = Output(UInt(Parameters.AddrWidth)) // inst_fetch.io.jump_address_id + val uses_rs1_id = Output(Bool()) // tells Control/Forwarding whether rs1 is valid for this instruction + val uses_rs2_id = Output(Bool()) // tells Control/Forwarding whether rs2 is valid for this instruction }) val opcode = io.instruction(6, 0) val funct3 = io.instruction(14, 12) @@ -159,9 +161,24 @@ class InstructionDecode extends Module { val rd = io.instruction(11, 7) val rs1 = io.instruction(19, 15) val rs2 = io.instruction(24, 20) + val uses_rs2 = (opcode === InstructionTypes.RM) || + (opcode === InstructionTypes.S) || + (opcode === InstructionTypes.B) - io.regs_reg1_read_address := Mux(opcode === Instructions.lui, 0.U(Parameters.PhysicalRegisterAddrWidth), rs1) - io.regs_reg2_read_address := rs2 + val uses_rs1 = !(opcode === Instructions.jal) && + !(opcode === Instructions.lui) && + !(opcode === Instructions.auipc) && + !(opcode === Instructions.fence) && + !(opcode === Instructions.csr && + (funct3 === InstructionsTypeCSR.csrrwi || + funct3 === InstructionsTypeCSR.csrrsi || + funct3 === InstructionsTypeCSR.csrrci)) + + io.uses_rs2_id := uses_rs2 + io.uses_rs1_id := uses_rs1 + + io.regs_reg1_read_address := Mux(uses_rs1, rs1, 0.U(Parameters.PhysicalRegisterAddrWidth)) + io.regs_reg2_read_address := Mux(uses_rs2, rs2, 0.U(Parameters.PhysicalRegisterAddrWidth)) io.ex_immediate := MuxLookup( opcode, Cat(Fill(20, io.instruction(31)), io.instruction(31, 20)) diff --git a/3-pipeline/src/test/scala/riscv/PipelineProgramTest.scala b/3-pipeline/src/test/scala/riscv/PipelineProgramTest.scala index 1982190c..b981f8c5 100644 --- a/3-pipeline/src/test/scala/riscv/PipelineProgramTest.scala +++ b/3-pipeline/src/test/scala/riscv/PipelineProgramTest.scala @@ -131,6 +131,11 @@ class PipelineProgramTest extends AnyFlatSpec with ChiselScalatestTester { c.clock.step() c.io.mem_debug_read_data.expect(20.U, "Multi-RAW Branch: mem[0x34] should be 20") + // Section 11: False Stall Prevention + c.io.mem_debug_read_address.poke(0x38.U) + c.clock.step() + c.io.mem_debug_read_data.expect(3.U, "False Stall: mem[0x38] should be 3") + // Validate cycle count (s0 register = x8) c.io.regs_debug_read_address.poke(8.U) c.clock.step()