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
    • Triage Meetings That Converge
    • Ownership and Hand-Offs
    • Bug Tracking and Severity
    • Debug Metrics That Help, and Ones That Hurt
    • Designing for Debuggability
  • Part 12 · Interview Prep5
Loading lesson

‹Part 11 · Debug as a Team
  1. Topics
  2. The Art of Hardware Debug
  3. Part 11 · Debug as a Team
  4. Ownership and Hand-Offs

Part 11 · Debug as a Team · Senior (S)

Ownership and Hand-Offs

Who owns a bug at each moment, how ownership moves between RTL, verification, physical design and the lab, and what a hand-off package must contain: commit, command, seed, simulator, first wrong cycle and a waveform window. Built for a real dbglab failure and accepted by a receiver who reproduces the first wrong cycle from scratch.

On this page

    Why this matters

    Every time a bug changes hands without its evidence, the receiver repeats the giver's last day: rebuilding the repro, finding the first wrong cycle again, re-ruling out the same suspects. A hand-off package that the receiver checks on arrival makes the move cost minutes and keeps exactly one person responsible.

    After this lesson you can: decide who owns a bug at each step, build a hand-off package with replay, signature, first wrong cycle, waveform window and open questions, and end the hand-off only when the receiver reproduces the first wrong cycle.

    Note

    Level: Senior (S) · Debug-loop step: Between Localise and Fix: the hunt changes hands

    Before this: Triage Meetings That Converge, Recording Everything to Replay, The Debug Log

    It is in your block now

    Tuesday. The verification engineer who took row S3 from the triage has walked it back from the stream pins to the power domain: a signal goes unknown while the domain is off. The power-domain owner sits in another team, eight hours away. The last time a bug crossed that boundary it took three days of messages before anyone looked at the RTL (an illustrative story, but a common one).

    What must travel with this bug so that the power-domain owner starts at the first wrong cycle tomorrow morning, not at the regression page?

    What people usually do, and why it fails

    The usual hand-off is a reassignment: the ticket gets a new owner and a comment, "looks like a power-domain issue, see the log". The receiver opens the log, finds one FAIL line, rebuilds the repro, wonders which simulator was used, and starts the walk-back from the symptom again. If they reach a different conclusion, the ticket goes back. Two people now half-own it, and neither is working on it.

    Two habits cause most of the waste. Ownership by block: the bug "belongs" to whoever owns the module where the symptom shows, so it moves when someone has a guess, not when the evidence says so. Handing over the failure instead of the hunt: the receiver gets the symptom, but not what the giver learned, which is the expensive part.

    The method: one owner, a package, an acceptance

    Who owns the bug

    • One person at a time, by name. A team cannot own a bug; a team's queue is where bugs wait.

    • The owner is whoever acts next. During Reproduce and Localise that is often verification; once the first wrong cycle is inside a block, the block's designer; for a timing question, physical design; for a part on a bench, the lab.

    • Ownership moves by acceptance. The giver offers, the receiver checks the package, then takes it. Until then the giver still owns it and still answers questions.

    • The tracker records each move with the step of the debug loop the bug is on, so anyone can see where it stands.

    What the package contains

    Field

    What goes in it

    Without it the receiver

    State of the hunt

    Which loop step, in one line

    does not know whether to reproduce, localise or fix

    Replay

    Full commit hash, command, seed, simulator and version, environment, local edits

    rebuilds the repro by guessing

    Signature

    The exact FAIL line in every simulator it was run in

    misses that one bug looks like two

    Frequency and history

    n of m seeds; last good and first bad commit, with the bisect log

    repeats the sweep and the bisect

    First wrong cycle

    Cycle, time, signal and value, with the probe or command that shows it

    walks back from the symptom again

    Waveform window

    How to make the dump, the time window and the six or so signals to look at

    opens a full dump and browses

    Ruled out

    What was excluded and by which result

    re-tests dead hypotheses

    Open question

    What the receiver is asked to decide or do

    does not know why they have it

    Most of these fields already exist if the giver kept a debug log and a replay record; the package is their summary, written for one reader.

    When the hand-off is done

    1. The receiver builds a fresh tree from the package's commit and runs its command: the same FAIL line.

    2. The receiver runs the probe or opens the window: the same first wrong cycle.

    3. Only then is ownership moved. Anything that differs goes back to the giver as a question, the same day.

    The picture

    Flowchart: the giver writes the package; the receiver replays it in a fresh tree. If the FAIL line or the first wrong cycle differs, a question goes back to the giver, who still owns the bug. If both match, the receiver accepts and the tracker's owner changes.
    A hand-off is a check, not a reassignment: ownership moves only after the receiver has seen the same failure and the same first wrong cycle.

    Do it: the package for S3

    Signature in both simulators

    Terminal
    $ tb/run.sh periph_power 1
    dbglab: test=periph_power seed=1
    FAIL test=periph_power seed=1 cycle=431: STREAM: pstream_valid is unknown
    $ tb/run.sh periph_power 1 verilator
    dbglab: test=periph_power seed=1
    FAIL test=periph_power seed=1 cycle=2421: WATCHDOG: no bus or stream progress for 2000 cycles; cpu_valid=0 cpu_ready=0 cpu_addr=00003008

    The same bug prints two different signatures: an unknown value in Icarus, a hang in Verilator, whose two-state supply model drives noise instead of X. If the package names only one, the receiver who runs the other simulator sees "a different bug". Both lines go in.

    History

    Terminal
    $ git bisect start HEAD v0.1
    $ git bisect run tb/run.sh periph_power 1
    ...
    $ git bisect log
    ...
    # bad: [122e88a7c8e74f9e3b29db8fad03dbe3fd3b7e9b] periph_pd: isolate only the outputs that leave the peripheral
    git bisect bad 122e88a7c8e74f9e3b29db8fad03dbe3fd3b7e9b
    # good: [b357bd84b7252b54097cac920e6a14cadda7503c] csr: no reset on the DMA address and data registers
    git bisect good b357bd84b7252b54097cac920e6a14cadda7503c
    # first bad commit: [122e88a7c8e74f9e3b29db8fad03dbe3fd3b7e9b] periph_pd: isolate only the outputs that leave the peripheral
    $ git show 122e88a
    ...
        rinc only feeds our own async FIFO, so it does not need a clamp.
    ...
    -  assign rinc      = iso_en ? 1'b0  : rinc_p;
    +  assign rinc      = rinc_p;          // internal: only feeds our own async FIFO

    First wrong cycle, with the probe that shows it

    The giver's probe prints the power controls, the sink's read-increment and the FIFO's read side on every edge of the peripheral clock. It is compiled next to the design and changes nothing (the exact command is in the Evidence box). It goes in the package as a file, because a cycle number the receiver cannot reproduce is only a claim.

    pd_probe.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // not-run: needs dbglab's sources and bench; compile it next to them (command in the Evidence box)
    // pd_probe: one line per pclk edge from +FROM to +TO (bus cycles): the power controls,
    // the sink's read-increment and the FIFO's read side. Reads the design, changes nothing.
    `timescale 1ns/1ps
    module pd_probe;
      int unsigned from_c, to_c;
      initial begin
        if (!$value$plusargs("FROM=%d", from_c)) from_c = 0;
        if (!$value$plusargs("TO=%d", to_c))     to_c   = 0;
        $display("  ns  cycle pwr_on iso_en rinc rempty rbin");
      end
      always @(posedge dbglab_tb.pclk) begin
        #0.5;
        if (dbglab_tb.cycle >= from_c && dbglab_tb.cycle <= to_c)
          $display("%4.0f  %5d    %b      %b     %b     %b    %h", $realtime, dbglab_tb.cycle,
            dbglab_tb.dut.u_per.u_pd.pwr_on, dbglab_tb.dut.u_per.u_pd.iso_en, dbglab_tb.dut.u_per.rinc,
            dbglab_tb.dut.u_per.rempty, dbglab_tb.dut.u_per.u_fifo.rbin);
      end
    endmodule
    Terminal
    $ ../probes/run-probe.sh ../probes/pd_probe.sv periph_power 1 +FROM=70 +TO=95
    dbglab: test=periph_power seed=1
      ns  cycle pwr_on iso_en rinc rempty rbin
     722     72    1      1     0     1    04
     759     76    0      1     x     1    04
     796     80    0      1     x     1    04
     833     83    0      1     x     0    04
     870     87    0      1     x     x    xx
     907     91    0      1     x     x    xx
     944     94    0      1     x     x    xx
    FAIL test=periph_power seed=1 cycle=431: STREAM: pstream_valid is unknown

    Line by line: at 722 ns the domain is on and isolated, and rinc is 0. At 759 ns (cycle 76) power goes off and rinc becomes X while `iso_en` is 1: that is the first wrong value. At 833 ns words arrive and the FIFO is no longer empty, so the X increment reaches the read pointer, which is X from 870 ns. The STREAM failure at cycle 431 is 355 cycles of consequence later.

    The waveform window

    Terminal
    $ DUMP=1 tb/run.sh periph_power 1
    dbglab: test=periph_power seed=1
    FAIL test=periph_power seed=1 cycle=431: STREAM: pstream_valid is unknown
    $ ls -l dbglab.vcd | awk '{print $5}'
    55057

    Here the whole dump is 55 KB, so it could be attached; on a real project the package gives the command, the window (700 to 950 ns) and the signals (pwr_on, iso_en, rinc, rempty, rbin, pstream_valid), and the receiver makes their own. Six lanes and 250 ns is a picture anyone can read; "see the dump" is not.

    The package as sent

    Field

    Content for S3

    State

    Localised: first wrong value found inside periph_pd. Asking the owner to decide the fix.

    Replay

    dbglab 49c5f04d1124aaeab1dc902884d49b7d1b4b8e75, clean tree; tb/run.sh periph_power 1; Icarus Verilog 13.0 and Verilator 5.048; no DUMP or XRAND

    Signature

    Icarus: FAIL test=periph_power seed=1 cycle=431: STREAM: pstream_valid is unknown. Verilator: ... cycle=2421: WATCHDOG: no bus or stream progress for 2000 cycles; ...

    Frequency and history

    Seeds 1 to 20, both simulators. First bad 122e88a, last good b357bd8 (bisect log attached)

    First wrong cycle

    Cycle 76, 759 ns: u_per.rinc = X with pwr_on = 0, iso_en = 1. Read pointer X from 870 ns. pd_probe.sv attached; +FROM=70 +TO=95

    Waveform window

    DUMP=1 on the replay command; 700 to 950 ns; the six signals above

    Ruled out

    Bench and stimulus (122e88a touches only rtl/periph_pd.sv); simulator (both fail); seed (all 20)

    Question

    122e88a says rinc needs no clamp because it feeds only our own FIFO, but the FIFO's read side is outside the switched domain. Should rinc be isolated, and is the power intent file consistent with the RTL?

    Notice what the package does not say: "the bug is the missing clamp". It states the first wrong value, the commit that changed it and the question only the owner can answer. The receiver may know a reason the clamp was removed that the giver does not.

    The receiver accepts

    Next morning, the power-domain owner builds the package's commit in a new folder and runs the three commands it names:

    Terminal
    $ R=$(mktemp -d)/recv && build-history.sh $R > /dev/null && cd $R && git checkout -q 49c5f04
    $ tb/run.sh periph_power 1; echo "exit $?"
    dbglab: test=periph_power seed=1
    FAIL test=periph_power seed=1 cycle=431: STREAM: pstream_valid is unknown
    exit 1
    $ tb/run.sh periph_power 1 verilator
    dbglab: test=periph_power seed=1
    FAIL test=periph_power seed=1 cycle=2421: WATCHDOG: no bus or stream progress for 2000 cycles; cpu_valid=0 cpu_ready=0 cpu_addr=00003008
    $ run-probe.sh pd_probe.sv periph_power 1 +FROM=72 +TO=87
    dbglab: test=periph_power seed=1
      ns  cycle pwr_on iso_en rinc rempty rbin
     722     72    1      1     0     1    04
     759     76    0      1     x     1    04
     796     80    0      1     x     1    04
     833     83    0      1     x     0    04
     870     87    0      1     x     x    xx
    FAIL test=periph_power seed=1 cycle=431: STREAM: pstream_valid is unknown

    Same lines, same cycle, same first wrong value, on a tree built from nothing. Now the tracker's owner changes, with a note: accepted, first wrong cycle reproduced, step: Fix. The receiver starts from cycle 76 and one line of RTL.

    Hand-offs between disciplines

    The package above is an RTL-to-RTL hand-off inside simulation. Across disciplines the fields stay the same, but what "replay" and "first wrong cycle" mean changes:

    From, to

    Replay means

    Extra fields

    Verification to design

    commit, test, seed, simulator

    the checker that fired and why it is trusted (when the bug is in the test)

    Design to physical design or timing

    netlist and constraints version, corner, the path or endpoint

    the functional symptom that made you suspect timing (timing-only bugs)

    Lab to design

    part identifier, board, voltage, frequency, temperature, the failing pattern

    register reads and scan dump at the failure (first failing test on silicon)

    Design to lab

    the experiment to run on the bench and the result that would decide it

    how to read the debug registers that answer it (bring-up failure triage)

    What the evidence says

    The receiver spent three commands and a minute to get exactly where the giver stopped, and took the bug with nothing to ask. The package cost the giver perhaps twenty minutes, most of it already in their log. Compare that with the three-day thread: the difference is not effort, it is whether the evidence travels.

    Traps

    Common pitfall

    One simulator's signature. A receiver who runs Verilator and sees a WATCHDOG hang while the package says "X on the stream" will reasonably think they have the wrong bug, and the hand-off bounces. Put every signature in, and say they are one bug.

    Common pitfall

    A screenshot instead of a command. A waveform picture shows what the giver looked at, not how to look again. It cannot be zoomed, extended or checked against a new build. Give the command, the window and the signals; attach a picture only as well.

    Common pitfall

    Ping-pong ownership. When a bug comes back with "not my block", ask what the receiver's acceptance run showed. If they reproduced the first wrong cycle and it is outside their logic, the package's localisation was wrong and the giver owns it again, with that new fact.

    Why this matters

    Senior lens. Make acceptance a status in the tracker: offered, then accepted. A bug that sits in "offered" for a day is visible, and its giver chases it. A silent reassignment is invisible until someone asks in triage why nothing happened.

    For cross-team and cross-time-zone hand-offs, write the package as if you will be unreachable for a day, because you will be. The test is the one from a bug report others can act on: the receiver can act without asking you anything.

    Interview

    Asked: "You have narrowed a failure to a block owned by another team. How do you hand it over?"

    Weak answer: I would assign the bug to the block owner with the log and a description of what I found, and offer to help if they have questions.

    Strong answer: I write a package: the exact commit, command, seed and simulator, the failure line in every simulator I ran, the first bad commit, and the first wrong cycle with the probe that shows it, a waveform window with the signals, what I ruled out, and the question I need them to answer. I do not tell them the fix. I keep ownership until they have rebuilt the tree and reproduced the first wrong cycle themselves; then the tracker moves to them.

    Follow-ups: (1) They reproduce the failure but not your first wrong cycle. What now? (2) What changes when the receiver is the lab and the bug is on silicon?

    Check yourself The receiver runs the package's command and gets PASS. Name three likely causes, and who owns the bug while you find out.

    A different commit or a dirty tree on either side, a different simulator or version, or an input the package forgot (an environment variable such as XRAND, a plusarg, a local edit). The giver still owns it: the hand-off failed its first check. The fix is a better replay record, then a new offer.

    Check yourself Why does the package for S3 ask a question instead of proposing the fix of restoring the clamp?

    Because the giver has localised the first wrong value, not proven the cause or the right fix. The owner knows the power intent, whether other outputs were also changed, and whether the commit's reasoning was right for some other configuration. A package that states facts and a question lets them decide; a proposed fix invites a quick yes that skips their knowledge.

    Remember

    • A bug has one owner at a time: the person who acts next. Ownership moves when the receiver accepts, not when the ticket is reassigned.

    • Hand over the hunt, not just the failure: replay, signature in every simulator, first wrong cycle with the probe that shows it, what is ruled out, the question.

    • The hand-off ends when the receiver reproduces the first wrong cycle on their own machine. Until then the giver still owns it.

    Rule of thumb: never reassign a bug the receiver has not yet reproduced.

    How this was checked

    dbglab at HEAD 49c5f04 (dbglab/build-history.sh), 2026-10-18. tb/run.sh periph_power 1 (Icarus Verilog 13.0) and the same with verilator (Verilator 5.048) give the two FAIL lines shown; seeds 1 to 20 fail in both. pd_probe.sv as shown, compiled with iverilog -g2012 -Wno-timescale -I tb -o build/probe.vvp <rtl/files.f> tb/bus_rules.sv tb/dbglab_tb.sv pd_probe.sv and run with vvp -n build/probe.vvp +TEST=periph_power +SEED=1 +FROM=70 +TO=95.

    git bisect start HEAD v0.1; git bisect run tb/run.sh periph_power 1; git bisect log (git 2.54.0) as shown; git show 122e88a as quoted. DUMP=1 tb/run.sh periph_power 1 wrote a 55,057-byte dbglab.vcd. Acceptance: a fresh build-history.sh into a new temporary folder, git checkout -q 49c5f04, the repro, the Verilator repro and the probe with +FROM=72 +TO=87, output as shown (script paths shortened in the listing).

    Limits: the people, teams and the earlier three-day hand-off are illustrative. Hand-offs to physical design and to the lab are described, not run: there is no netlist flow or silicon here. The package records one bug; it does not prove the receiver's hypothesis.

    Keep going

    • Practice: Seed reproducibility: failing to log random seeds breaks debug

    • Practice: Seed lineage and reproducibility

    • Interview: run a mock interview on this topic

    Related topics

    • Part 11 · Debug as a Team

    • Triage meetings that converge

    • Recording everything to replay

    • Bisection in space: the same failure localised to the power domain

    • Bring-up: failure triage in the lab

    Previous

    99 / 107

    Next: Bug Tracking and Severity

    Up next

    Up nextBug Tracking and Severity

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Debug Metrics That Help, and Ones That Hurt
    • Designing for Debuggability