Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Part 0 · What Debugging Is6
  • Part 1 · Reproduce8
  • Part 2 · Observe9
  • Part 3 · Localise10
  • Part 4 · Hypothesise and Test6
  • Part 5 · Bug Classes16
  • Part 6 · Debug by Stage9
  • Part 7 · Hangs and Deadlocks7
  • Part 8 · Intermittent Failures7
  • Part 9 · Fix, Prove, Prevent7
  • Part 10 · Case Studies12
  • Part 11 · Debug as a Team5
  • Part 12 · Interview Prep5
    • Find the Bug in This Waveform
    • The Chip Hangs: What Do You Do?
    • A Test Fails Once in 500 Runs
    • How Do You Know Your Fix Works?
    • Mock Debug Interview
Loading lesson

‹Part 12 · Interview Prep
  1. Topics
  2. The Art of Hardware Debug
  3. Part 12 · Interview Prep
  4. Mock Debug Interview

Part 12 · Interview Prep · Senior (S)

Mock Debug Interview

A full 30-minute mock debug interview on a real dbglab failure: the scenario, the interviewer's and the candidate's turns, a real exhibit at every step, where weak candidates lose the thread, and a scoring rubric that separates a senior answer from a principal one.

On this page

    Why this matters

    Knowing the method is not the same as using it under a clock with someone watching. A rehearsed mock shows where answers drift into guessing, and the rubric shows what moves a strong senior answer to a principal one.

    After this lesson you can: run a 30-minute debug interview as either side: ask the report questions, drive the loop from exhibits, state predictions, close with proof and escape analysis, and score an answer against a senior and a principal bar.

    Note

    Level: Senior (S) · Debug-loop step: The whole loop

    Before this: The Debug Loop, Bisection in History

    How to use this mock

    Read it with a partner if you can: one plays the interviewer and hands over each exhibit only when asked for it. Alone, cover each candidate turn and say your own answer aloud before reading on. The times are a guide for a 30-minute slot. The rubric at the end scores both a senior and a principal bar. If you have read the reset-value case study, you know the answer: play the interviewer instead, and score a partner against the rubric.

    This mock is won at Report (the first questions), Localise (the first wrong value and the bisect) and Prevent (the escape analysis).

    Minutes 0 to 3: the scenario

    Interviewer: "You join a team on a small SoC: a bus, a DMA, a CSR block with interrupts. Yesterday a refactor of the CSR block went in; its message says no functional change intended. This morning one test is red. Here is the line. Debug it with me; ask for anything you need."

    Terminal
    $ tb/run.sh irq_w1c 1
    dbglab: test=irq_w1c seed=1
    FAIL test=irq_w1c seed=1 cycle=84: IRQ: irq=1 while CTRL=00 enables no interrupt

    Tip

    Try it first. Before reading the candidate's turn, write down your first three questions.

    Minutes 3 to 8: the report

    Candidate: "Let me read it back. At cycle 84 the interrupt line is high, and the test believes CTRL is zero, so no interrupt is enabled. Either the RTL's CTRL is not zero, or the interrupt logic ignores the enables. Three questions. Does it fail on every seed? Does it fail in both simulators we have? And is this the only red test, or did others change too?"

    Interviewer: "Here you go."

    Terminal
    $ for s in $(seq 1 20); do tb/run.sh irq_w1c $s | tail -1; done | sed -E 's/seed=[0-9]+ //' | sort | uniq -c
      20 FAIL test=irq_w1c cycle=84: IRQ: irq=1 while CTRL=00 enables no interrupt
    $ tb/run.sh irq_w1c 1 verilator
    dbglab: test=irq_w1c seed=1
    FAIL test=irq_w1c seed=1 cycle=84: IRQ: irq=1 while CTRL=00 enables no interrupt

    Candidate: "Every seed, same cycle, both simulators. So it is deterministic and does not depend on the random stimulus: my repro is seed 1. That also makes a race or a tool problem unlikely. I would still like to know about other tests."

    Interviewer: "The DMA and peripheral tests have had failures for a while; they are known. This one is new."

    Note

    Where weak candidates go wrong. They start with "I would open the waveform at cycle 84". That skips the two questions that cost nothing and remove whole classes of cause: seed dependence and simulator dependence. The interviewer marks Report and Reproduce as missed.

    Minutes 8 to 14: observe and localise

    Candidate: "The test says CTRL is zero, but that is the bench's belief, not the design's. I want the design's view: the CTRL register at reset release, and done and irq when they change. If CTRL is already wrong at reset, everything later is a consequence. My prediction: since the test never writes CTRL before the first DMA finishes, I expect CTRL to be non-zero from reset."

    Interviewer: "Fine. Here is a probe that prints that."

    csr_probe.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // not-run: needs dbglab's sources and bench; compile it next to them (command in the Evidence box)
    `timescale 1ns/1ps
    // csr_probe: prints CTRL, STATUS.done and irq at reset release and whenever irq or done changes.
    // Compiled next to dbglab; reads the design by hierarchical names and changes nothing.
    module csr_probe;
      logic last_irq = 1'b0, last_done = 1'b0, seen_rst = 1'b0;
      always @(posedge dbglab_tb.clk) begin
        #0.5;
        if (dbglab_tb.rst_n && !seen_rst) begin
          seen_rst = 1'b1;
          $display("cycle %0d: reset released, ctrl_q=%h", dbglab_tb.cycle, dbglab_tb.dut.u_csr.ctrl_q);
        end
        if (dbglab_tb.rst_n && (dbglab_tb.dut.u_csr.irq !== last_irq || dbglab_tb.dut.u_csr.done_q !== last_done))
          $display("cycle %0d: done_q=%b irq=%b ctrl_q=%h", dbglab_tb.cycle, dbglab_tb.dut.u_csr.done_q,
                   dbglab_tb.dut.u_csr.irq, dbglab_tb.dut.u_csr.ctrl_q);
        last_irq = dbglab_tb.dut.u_csr.irq; last_done = dbglab_tb.dut.u_csr.done_q;
      end
    endmodule
    Terminal
    $ probes/run-probe.sh dbglab probes/csr_probe.sv irq_w1c 1
    dbglab: test=irq_w1c seed=1
    cycle 5: reset released, ctrl_q=01
    cycle 84: done_q=1 irq=1 ctrl_q=01
    FAIL test=irq_w1c seed=1 cycle=84: IRQ: irq=1 while CTRL=00 enables no interrupt

    (Reset release shows as cycle 5 here because this probe samples just after the clock edge; the case study's probe samples at the edge and says 4.)

    Candidate: "The prediction held. CTRL is 01 the moment reset is released: bit 0, the done-interrupt enable, is set. The register map says CTRL resets to 0. When the first DMA run finishes at cycle 84, done sets and the enabled interrupt fires, exactly as the logic should with that CTRL. So the interrupt logic is fine; the first wrong value is ctrl_q at reset. That is the reset branch, or the value it loads."

    Interviewer: "Good. Why did you not look at the interrupt logic first? That is where the symptom is."

    Candidate: "Because the message itself said irq was high with an enable the bench thought was off. The cheapest experiment was one that split the enable is wrong from the OR of the enables is wrong. Printing CTRL did that in one run."

    Note

    Where weak candidates go wrong. They read the interrupt logic line by line, find it correct, and get stuck, because the bug is not there. Or they "fix" it by masking irq until software writes CTRL. That hides the symptom and leaves a register that resets to the wrong value. See symptom, fault and cause.

    Minutes 14 to 18: localise in history

    Candidate: "It passed before yesterday's refactor, and the refactor touched reset values. I would not assume; I would bisect from the last release, which takes a handful of runs. Prediction: the first bad commit is the refactor."

    Terminal
    $ git bisect start HEAD v0.1 && git bisect run tb/run.sh irq_w1c 1
    ...
    [a60b6f711da8e202872a42f92fbdedcbcb811ab8] periph: power gate model with output isolation
    running 'tb/run.sh' 'irq_w1c' '1'
    dbglab: test=irq_w1c seed=1
    PASS test=irq_w1c seed=1
    ...
    [026f8bfe1f4ed59ec2d8f33c866509ac9d197dc1] csr: collect register reset values in dbglab_pkg
    running 'tb/run.sh' 'irq_w1c' '1'
    dbglab: test=irq_w1c seed=1
    FAIL test=irq_w1c seed=1 cycle=116: IRQ: irq=1 while CTRL=00 enables no interrupt
    ...
    026f8bfe1f4ed59ec2d8f33c866509ac9d197dc1 is the first bad commit
    ...
    bisect found first bad commit
    $ git show 026f8bf -- rtl/dbglab_pkg.sv
    ...
    +  // Reset values.
    +  localparam logic [31:0] SCRATCH_RST = 32'h0000_0000;
    +  localparam logic [7:0]  CTRL_RST    = 8'h01;
    +  localparam logic [31:0] SRC_RST     = 32'h0000_0000;
    ...

    Candidate: "Four runs, and it is the refactor. The cycle number differs at that commit, 116 instead of 84, because a later commit made the DMA faster; the message is the same, so it is the same bug. The diff moved the reset literals into a package table, and CTRL's entry is 01 where the old code had 0. One wrong character in a table."

    Interviewer: "How sure are you it is that line and not something else in the commit?"

    Candidate: "The probe showed ctrl_q = 01 at reset, and this is the line that sets it. The test of that is the fix: change the one value, and predict that irq_w1c passes on every seed in both simulators and that nothing else in the regression changes."

    Minutes 18 to 22: fix and prove

    design.sv
    // snippet: the fix, rtl/dbglab_pkg.sv line 39
      localparam logic [7:0]  CTRL_RST    = 8'h00;
    Terminal
    $ tb/run.sh irq_w1c 1
    dbglab: test=irq_w1c seed=1
    PASS test=irq_w1c seed=1 cycles=316 time=3156 ns
    $ tb/run.sh irq_w1c 1 verilator
    dbglab: test=irq_w1c seed=1
    PASS test=irq_w1c seed=1 cycles=316 time=3156 ns
    $ for s in $(seq 1 20); do tb/run.sh irq_w1c $s | tail -1; done | cut -c1-4 | uniq -c
      20 PASS
    $ tb/regress.sh icarus 1 2 3 | grep -E "FAIL|failing"
    dma_burst_backpressure   seed 1      FAIL
    ...
    periph_power             seed 3      FAIL
    6 failing run(s)

    Candidate: "Every prediction held: the repro passes in both simulators, all twenty seeds pass, and the regression went from nine failing runs to six, the six being the known DMA and peripheral failures. Since this test does not depend on the seed, twenty seeds add little; the real proof is that the first wrong value is gone and nothing else moved."

    Note

    Where weak candidates go wrong. They stop at "the test passes". The interviewer then asks how do you know?, and the answer has to be built under pressure. Saying the prediction before the run, and what the sweep does and does not show, is what how do you know your fix works? rehearses.

    Minutes 22 to 27: why did it escape?

    Interviewer: "The refactor was reviewed and the regression ran. Why did this take a day to find, and what would you change?"

    Candidate: "Two reasons. Only irq_w1c sees CTRL's reset value: every other test writes CTRL before its first DMA run, and the smoke test never reads CTRL after reset. And the review read a refactor labelled no functional change, which invites skimming. One thing went right: the bench keeps its own reset values. Had it imported the package's table, model and RTL would have agreed and nothing would have failed."

    Interviewer: "So what test would have caught it the day it was written?"

    Candidate: "A reset-value test: read every register with a documented reset value before anything writes it, and compare with the register map, not with the RTL. It is three reads. I would expect it to fail on CTRL now."

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // not-run: added to tb/tests.svh in a scratch copy of dbglab (and dispatched from dbglab_tb.sv)
    // reset_values (scratch copy only): every register with a documented reset value, read before
    // anything writes it. Expected values come from the register map in README.md, not from the RTL.
    task automatic t_reset_values();
      rd_expect(CSR_BASE + CSR_CTRL,   32'h0, "CTRL reset value");
      rd_expect(CSR_BASE + CSR_STATUS, 32'h0, "STATUS reset value");
      rd_expect(CSR_BASE + CSR_FILL,   32'h0, "FILL reset value");
    endtask
    Terminal
    $ tb/run.sh reset_values 1                       # HEAD plus the test
    dbglab: test=reset_values seed=1
    FAIL test=reset_values seed=1 cycle=10: CTRL reset value: read 00002008 = 00000001, expected 00000000
    $ cd ../rv1; tb/run.sh reset_values 1            # with the CTRL fix
    dbglab: test=reset_values seed=1
    FAIL test=reset_values seed=1 cycle=16: FILL reset value: read 00002024 = xxxxxxxx, expected 00000000
    $ tb/run.sh reset_values 1 verilator
    dbglab: test=reset_values seed=1
    PASS test=reset_values seed=1 cycles=16 time=156 ns
    $ XRAND=1 tb/run.sh reset_values 1 verilator
    dbglab: test=reset_values seed=1
    FAIL test=reset_values seed=1 cycle=16: FILL reset value: read 00002024 = 8f27a164, expected 00000000

    Candidate: "It fails on CTRL at cycle 10 instead of 84. And with CTRL fixed it finds a second bug: FILL, documented to reset to 0, reads X in Icarus. Verilator passes because its flops start at 0, and fails with random start values. That is a separate bug, an unreset register, and I would open a separate ticket rather than fold it into this fix."

    Interviewer: "Would you generate the RTL reset values and the bench's expected values from one register description, so they cannot disagree?"

    Candidate: "Generate both from the specification, yes, with the generated reset test kept independent of the RTL. Generating the bench's values from the RTL's table would make them agree by construction, which is exactly the check that caught this bug. And a generated table can still hold a wrong entry; the reset test against the document is what catches that."

    Minutes 27 to 30: the summary

    Interviewer: "Give me the two-sentence version for the bug record."

    Candidate: "Commit 026f8bf moved CSR reset values into a package and set CTRL's to 01, enabling the done interrupt from reset; irq_w1c fails at cycle 84, every seed, both simulators, and the fix restores 00, after which it passes and the regression is otherwise unchanged. A new reset_values test fails on this bug at cycle 10 and also exposes an unreset FILL register, filed separately."

    The scoring rubric

    Interviewers score the method, not whether you found the line. A candidate who reaches the right commit by guessing scores lower than one who runs out of time halfway with every step justified.

    Dimension

    Misses the bar

    Senior bar

    Principal bar

    Report

    Starts debugging the message

    Asks about seeds, simulators and other tests before looking

    Also asks who is affected and what it blocks (software that relies on CTRL's reset value, the release), and sorts known failures from new ones

    Reproduce

    Reruns until it fails

    Names one deterministic command and checks a second simulator

    Picks the cheapest stage and command that keeps the failure, and says what the determinism rules out (races, tools) before spending a run

    Observe and localise

    Reads the logic where the symptom is

    Finds the first wrong value (CTRL at reset) with one experiment

    Chooses the experiment that splits the suspects, and says why

    Hypothesise and test

    Explains after the result

    States a prediction before each exhibit

    Predicts what a wrong fix would do too, and what the bisect will name

    History

    Reads every recent commit

    Bisects and reads the guilty diff

    Explains the cycle-number difference at the old commit instead of being thrown by it

    Fix and prove

    "The test passes"

    Repro in both simulators, seeds, regression unchanged

    Says which evidence is strong and which (the seed sweep) adds little here

    Prevent and escape

    "Add a test"

    A reset-value test against the register map

    Why it escaped (coverage of reset values, review of a labelled refactor), the independence of model and RTL, and the cost of the process change

    Communication

    Silent or a stream of observations

    Names the loop step and its output as they go

    States the decision for others: severity, who must be told (anyone whose software relies on CTRL's reset value), the second bug's owner, and a two-sentence summary

    This candidate meets the senior bar on every row and the principal bar on most: the split experiment in minute 8, the explanation of cycle 116, the independent-model point and the second bug filed separately are the principal signals. To score principal on every row, they would also say what the process change costs (a reset test per register block, run in every regression) and who owns it, and tell anyone whose software relies on CTRL's reset value that it changes back, which is Part 11's territory.

    Traps

    Common pitfall

    Racing to the answer. Naming the bug in minute 5 from a hunch scores less than the same answer reached with a prediction and an experiment. If you think you know, say the hypothesis and the experiment that would prove you wrong.

    Common pitfall

    Folding a second bug into the first fix. The reset test found FILL as well. Fixing both in one change makes the proof of each weaker. Name it, file it, keep going.

    Why this matters

    Principal lens. The interviewer's last question, one source for RTL and bench, is a design-for-debug question in disguise. The principal answer keeps two independent views of every fact that matters (the spec and the RTL, the model and the design) and a cheap test that compares them. Making them one view removes the check that finds the bug.

    Interview

    Asked: "We have 30 minutes. Pick a bug you found that you are proud of and walk me through it."

    Weak answer: There was a really hard bug in our DMA once. It took two weeks. In the end it was a timing issue in the FIFO and we fixed it by adding a register.

    Strong answer: Name the symptom and how often it happened, then walk the loop: how you made it reproduce, the first wrong value and how you found it, the hypothesis and the experiment that could have proven it wrong, the fix, the evidence that it worked, and what you added so it could not come back. Keep it to three minutes and leave the interviewer room to dig. Know the numbers (cycles, seeds, rates) and say what you would do differently now.

    Follow-ups: (1) What was the experiment that changed your mind? (2) What would have caught it a month earlier?

    Check yourself In minute 14 the candidate saw cycle 116 at the old commit where HEAD says 84. A weaker candidate concludes the bisect found a different bug. What is the right reading, and how would you check it quickly?

    The message is identical except for the cycle, and the cycle is when the first DMA run finishes, which depends on the DMA's speed. A later commit made the DMA faster, so the same bug shows earlier at HEAD. To check: run the probe at the old commit. It prints ctrl_q=01 at reset release and irq rising with done at cycle 116: same first wrong value, same bug.

    Check yourself Halfway through, the interviewer says: "Suppose the probe had shown CTRL = 00 at reset." What would you have said?

    Then the enable is right and the interrupt logic is wrong: irq rises with done although CTRL enables nothing. The next experiment would print the terms of the irq expression at cycle 84 (done and each enable bit) to find which one passes, and the next hop is that line in csr_block. The point of the split experiment is that either result tells you where to go.

    Remember

    • Spend the first minutes on the report. Questions about seeds, commits and other tests save the next twenty minutes.

    • Say a prediction before each exhibit. The interviewer scores the reasoning that chose the experiment, not the luck of the result.

    • The principal answer does not stop at the fix: why it escaped, what test would have caught it, and what the process change costs.

    Rule of thumb: in an interview, name the loop step you are on and the output that will finish it.

    How this was checked

    dbglab at HEAD 49c5f04 (build-history.sh), 2026-10-18. tb/run.sh irq_w1c 1 fails at cycle 84 in Icarus Verilog 13.0 and Verilator 5.048; seeds 1 to 20 give the same line. csr_probe.sv compiled next to dbglab by probes/run-probe.sh (Icarus). In a scratch copy: git bisect start HEAD v0.1, git bisect run tb/run.sh irq_w1c 1 (git 2.54.0), four test runs, first bad commit 026f8bf; csr_probe at that commit prints ctrl_q=01 at reset and irq at cycle 116.

    Fix in scratch copy b1fix (CTRL_RST = 8'h00): irq_w1c passes seeds 1 to 20 in Icarus and seed 1 in Verilator; tb/regress.sh icarus 1 2 3 9 failing runs at HEAD, 6 with the fix. A reset_values test added to scratch copies of the bench (the task shown and one dispatch line): fails on CTRL at HEAD in both simulators; with B1 fixed fails on FILL in Icarus (X) and in Verilator with XRAND=1, passes in Verilator without it; passes with B1 and B4 fixed.

    Limits: one interview scenario on one small design. The dialogue and the rubric are illustrative; the exhibits are real runs. A real interview would rarely hand over a working simulator, so exhibits stand in for what the interviewer tells you.

    Keep going

    • Practice: Reset value testing of registers

    • Practice: Modelling W1C status registers

    • Practice: Reset strategies in RTL

    • Code lab: RAL reset values

    • Code lab: Maskable IRQ register

    • Code lab: Regression triage

    • Interview: run a mock interview on this topic

    Related topics

    • Part 12 · Interview Prep

    • Bisection in history

    • Configuration and register-map bugs

    • X and reset bugs

    • Part 11: debug as a team

    • RTL: debug and ECO interview answers

    Previous

    107 / 107

    Up next

    You reached the end of this course — nicely done.

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Find the Bug in This Waveform
    • The Chip Hangs: What Do You Do?
    • A Test Fails Once in 500 Runs