Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions 3-pipeline/csrc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 :=
Expand All @@ -39,4 +39,4 @@ update: $(BINS)
cp -f $(BINS) ../src/main/resources

clean:
$(RM) *.o *.elf *.asmbin
$(RM) *.o *.elf *.asmbin
16 changes: 15 additions & 1 deletion 3-pipeline/csrc/hazard_extended.S
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -120,4 +134,4 @@ skip2_end:
# s0 (x8) = cycle count

loop:
j loop
j loop
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 19 additions & 11 deletions 3-pipeline/src/main/scala/riscv/core/fivestage_final/Control.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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)
Expand All @@ -104,11 +110,12 @@ 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
Expand All @@ -125,10 +132,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]
Expand All @@ -140,17 +148,17 @@ 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) ============
// Branch resolved in ID stage - only 1 cycle penalty
// 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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,33 @@ 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)
val funct7 = io.instruction(31, 25)
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))
Expand Down
5 changes: 5 additions & 0 deletions 3-pipeline/src/test/scala/riscv/PipelineProgramTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down