← RTL Design Interviews

Interview Question · Debug

The Forwarding Bug That Passed Back-to-Back Tests

L2 Mid-LevelL3 Seniorcomputer architecturehazardsverification

The Question

The interviewer asks

Every directed hazard test passes, but a random program fails against the instruction set simulator. Here is the log. What is the bug, and how should the tests have caught it?

log
test dist1: 3 of 3 results match the ISS
test mixed: mismatch at instr 2: r4 expected 7 got 3
    instr 0: add r1, r2, r3
    instr 1: add r6, r7, r7
    instr 2: add r4, r1, r2
Reveal the Model Answer

Instruction 2 reads r1, written two instructions earlier, so it needs the MEM/WB forwarding path. The design has only the EX/MEM path for distance 1, so it read the stale r1. The directed tests used only back-to-back dependencies. Coverage should cross the producer type with the distance, 1 to 3, for each source operand. Then an empty bin would have shown the missing case.

Five Stages and Two Forwarding PathsArrows carry instructions down the pipeline and results back to the ALU input. The forward mux picks the newest copy of a register.
Five stages and two forwarding pathsArrows carry instructions down the pipeline and results back to the ALU input. The forward mux picks the newest copy of a register. component IF fetch the instruction component ID decode, readregisters component EX the ALU component MEM load or store data component WB write the registerfile component Forward mux picks the ALUoperand EX/MEM result:distance 1MEM/WB result:distance 2operand
  1. A RAW hazard

    The next instruction reads a register in ID before the producer has written it in WB.

  2. Forward from EX/MEM

    An ALU result exists at the end of EX. The forward mux hands it to the very next instruction.

  3. Forward from MEM/WB

    Two instructions later, the result sits in MEM/WB. A second path serves that distance.

  4. Load-use

    A load has its data only at the end of MEM, one stage too late for the next EX. That case still stalls one cycle.

Read the diagram as text
  • IF: fetch the instruction
  • ID: decode, read registers
  • EX: the ALU
  • MEM: load or store data
  • WB: write the register file
  • Forward mux: picks the ALU operand
  • IF → ID
  • ID → EX
  • EX → MEM
  • MEM → WB
  • MEM → Forward mux: EX/MEM result: distance 1
  • WB → Forward mux: MEM/WB result: distance 2
  • Forward mux → EX: operand

The design had only the distance-1 path from EX/MEM. The MEM/WB path for distance 2 was missing.

The failing sequence, cycle by cycle
InstructionC1C2C3C4C5C6C7
0: writes r1IFIDEXMEMWB
1: no dependenceIFIDEXMEMWB
2: reads r1IFIDEXMEMWB

In C5 instruction 2 needs r1 in EX while instruction 0 holds it in MEM/WB. Without that path, EX reads the stale value.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Mid-Level)

A good mid-level answer reads the distance from the listing and names the missing forwarding path. It then asks what the directed tests covered.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

WhyWhat IfCompareScale It

  1. The Forwarding Bug That Passed Back-to-Back Tests
  2. WhyCrossing Producer Type with DistanceL2 Mid-Level

    Why should hazard coverage cross the producer type with the dependency distance?

    Reveal the Model Answer

    Each forwarding path serves one distance and one kind of producer. A load at distance 1 stalls, an ALU result at distance 2 uses MEM/WB, and distance 3 goes through the register file. Crossing type, distance and operand gives one bin per path. Then an untested path shows up as an empty bin.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Code coverage on the forwarding mux is enough."

    The mux lines can be hit by distance-1 traffic alone. Only a functional cross shows which distances and producers were exercised.

    Source Mehta, ASIC/SoC Functional Design Verification (2018) ch. 7.1 Difference between code coverage and functional coverage

  3. What IfForwarding from an Instruction That Is Being FlushedL2 Mid-LevelL3 Senior

    What if a branch flush or an interrupt lands while a wrong-path instruction is in EX with a result to forward?

    Reveal the Model Answer

    The flushed instruction must not forward, and must not write the register file. If the forward select ignores the flush, the next correct instruction can take a wrong-path value. Test it by placing dependent instructions right after a mispredicted branch and right at an interrupt. Cover those as their own bins.

    Self-Check

    Tick each key point you covered in your answer.

    Missed Corner

    "Flushed instructions are killed in WB, so nothing escapes."

    Forwarding takes values before WB. A result in EX/MEM can reach the next instruction before the kill takes effect.

    Source Hennessy and Patterson, Computer Architecture: A Quantitative Approach (5th ed.), App. C, Pipelining: Basic and Intermediate Concepts

  4. CompareISS Compare vs Assertions on the Forwarding UnitL2 Mid-LevelL3 Senior

    Compare checking the pipeline against an ISS with writing assertions on the forwarding unit.

    Reveal the Model Answer

    An ISS compare checks architectural results end to end, so it catches any wrong result. But it reports far from the cause. An assertion checks a local rule, such as "when EX reads the register MEM/WB is writing, the forward select picks MEM/WB". It fires at the cycle of the bug. Use both: the ISS for completeness, assertions to localize.

    Self-Check

    Tick each key point you covered in your answer.

    Wrong Altitude

    "Assertions are the verification team's job, not the designer's."

    The designer knows the micro-architecture rules best. Assertions on them are written while designing.

    Source Mehta, SystemVerilog Assertions and Functional Coverage (3rd ed., 2020) ch. 2.7-2.9 (who writes which assertions)

  5. Scale ItCPU Pipeline Sign-OffL3 SeniorL4 Staff

    Scale it: what would make you sign off the pipeline for tape-out?

    Reveal the Model Answer

    The plan comes first: every hazard, flush and exception case written as a coverage item. Sign-off needs those items closed, millions of random instructions with zero ISS mismatches, and assertions on the pipeline control that never fire. Formal proofs on the forwarding and stall logic help where tests are thin. Watch the bug-find rate fall to near zero over several weeks.

    Self-Check

    Tick each key point you covered in your answer.

    Wrong Altitude

    "We ran a billion random instructions with no mismatch."

    Volume alone does not show which hazards were hit. Without closed coverage, a rare case can still be untested.

    Source Mehta, ASIC/SoC Functional Design Verification (2018) ch. 2.2 A comprehensive verification plan

What People Get Wrong

Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.

Missed Corner

"All hazard tests pass, so the forwarding unit is done."

The tests only covered distance 1. The twin passes them and fails the first dependency at distance 2.

Run It

A pipeline model with only the EX/MEM forwarding path, compared against a sequential model.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: test dist1: 3 of 3 results match the ISS test mixed: mismatch at instr 2: r4 expected 7 got 3 FAIL: back-to-back tests passed, but a dependency at distance 2 read a stale register
See the Fix
module tb;
  // Five-stage pipeline, no stalls, ALU instructions only (add rd, ra, rb).
  // Instruction i is in EX at cycle i+2 and writes the register file in WB at cycle i+4.
  // The register file is written before it is read in the same cycle, so ID of instruction i sees
  // the results of instructions up to i-3. Distances 1 and 2 need forwarding into EX.
  int rd [8], ra [8], rb [8], n;
  int gold [8], rf [8], res [8];
  function automatic int operand(input int i, input int r);
    if (i >= 1 && rd[i-1] == r) return res[i-1];                       // EX/MEM path: distance 1
    if (i >= 2 && rd[i-2] == r) return res[i-2];                       // MEM/WB path: distance 2
    return rf[r];
  endfunction
  task automatic run_test(input string name);
    int bad, ref_v;
    for (int r = 0; r < 8; r++) begin gold[r] = r; rf[r] = r; end
    bad = 0;
    for (int i = 0; i < n; i++) begin
      if (i >= 3) rf[rd[i-3]] = res[i-3];                               // WB of instruction i-3
      res[i] = operand(i, ra[i]) + operand(i, rb[i]);                   // EX of instruction i
      ref_v = gold[ra[i]] + gold[rb[i]];                                // the sequential model (ISS)
      gold[rd[i]] = ref_v;
      if (res[i] != ref_v && bad == 0) begin
        bad = 1;
        $display("test %s: mismatch at instr %0d: r%0d expected %0d got %0d", name, i, rd[i], ref_v, res[i]);
      end
    end
    if (!bad) $display("test %s: %0d of %0d results match the ISS", name, n, n);
  endtask
  initial begin
    rd[0] = 1; ra[0] = 2; rb[0] = 3;
    rd[1] = 4; ra[1] = 1; rb[1] = 1;
    rd[2] = 5; ra[2] = 4; rb[2] = 2;
    n = 3; run_test("dist1");
    rd[0] = 1; ra[0] = 2; rb[0] = 3;
    rd[1] = 6; ra[1] = 7; rb[1] = 7;
    rd[2] = 4; ra[2] = 1; rb[2] = 2;
    n = 3; run_test("mixed");
    $display("PASS: with both forwarding paths, distance 1 and distance 2 match the ISS");
  end
endmodule

Prints: test mixed: 3 of 3 results match the ISS PASS: with both forwarding paths, distance 1 and distance 2 match the ISS

Half-Truth

"The ISS compare found it, so the method works."

It found it late and far from the cause, in a random program. A coverage hole would have flagged the untested distance much earlier.

Source Mehta, ASIC/SoC Functional Design Verification (2018) ch. 18 Cache memory subsystem verification: ISS based

Delivery

Starts debugging the adder because the sum is wrong.

The sum used a wrong operand. Check which register value reached EX before looking at the arithmetic.

Learn It, Then Prove It